Day27 前半のゴール
「“複雑そうに見えるJOIN+集計”を、怖がらずに分解して読めるようになる」
Day25〜26 で、
顧客・商品・注文・注文明細を分けて設計し、
正規化の感覚もつかんできました。
Day27 のテーマは 実践クエリ:複雑なJOINと集計。
前半のゴールはこうです。
複数テーブルをまたいだ JOIN を、順番に“たどる”感覚を身につける
JOIN と GROUP BY が同時に出てきても、「何を単位に集計しているか」を説明できる
一見ゴチャっとした SQL を、「部品」に分解して理解できる
いきなり難しいクエリを書くというより、
「複雑そうに見えるクエリを、どう分解して考えるか」にフォーカスします。
まずは“土台のスキーマ”をもう一度はっきりさせる
「どのテーブルが、何の“軸”なのかを忘れない」
前提として使うスキーマを、もう一度整理します。
CREATE TABLE customers (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
email TEXT NOT NULL UNIQUE,
phone TEXT,
registered_at TEXT NOT NULL,
status INTEGER NOT NULL DEFAULT 1
);
CREATE TABLE products (
id INTEGER PRIMARY KEY,
name TEXT NOT NULL,
price INTEGER NOT NULL,
status INTEGER NOT NULL DEFAULT 1
);
CREATE TABLE orders (
id INTEGER PRIMARY KEY,
customer_id INTEGER NOT NULL,
ordered_at TEXT NOT NULL,
status INTEGER NOT NULL DEFAULT 1
);
CREATE TABLE order_items (
id INTEGER PRIMARY KEY,
order_id INTEGER NOT NULL,
product_id INTEGER NOT NULL,
quantity INTEGER NOT NULL,
unit_price INTEGER NOT NULL
);
SQLここで大事なのは、「役割のイメージ」です。
customers は「誰が」
products は「何を」
orders は「いつ・誰が注文したか」
order_items は「その注文で何を何個・いくらで売ったか」
JOIN と集計を考えるときは、
この“役割”を頭の片隅に置いたまま進むと、迷子になりにくくなります。
JOIN が複雑に見える理由
「一気に全部見ようとするから、脳がパンクする」
複雑な JOIN が怖く感じる一番の理由は、
「FROM〜JOIN〜JOIN〜JOIN…」を一気に読もうとするからです。
例えば、こんなクエリをいきなり見せられるとします。
SELECT
c.name,
p.name,
SUM(oi.quantity * oi.unit_price)
FROM customers c
JOIN orders o
ON c.id = o.customer_id
JOIN order_items oi
ON o.id = oi.order_id
JOIN products p
ON oi.product_id = p.id
GROUP BY
c.name,
p.name;
SQL「顧客×商品ごとの売上合計」を出しているクエリですが、
最初は正直、目が滑ります。
ここでやるべきことは、
一気に理解しようとしないこと です。
JOIN は「テーブル同士を線でつなぐ」だけなので、
1本ずつたどれば必ず理解できます。
JOIN を“1本ずつたどる”練習
「FROM から順番に、“今どのテーブルまで来たか”を意識する」
さっきのクエリを、JOIN の流れだけに注目して分解してみます。
FROM customers c
JOIN orders o
ON c.id = o.customer_id
JOIN order_items oi
ON o.id = oi.order_id
JOIN products p
ON oi.product_id = p.id
SQLこれを、頭の中でこうたどります。
最初は customers(顧客)だけの世界
そこに orders を JOIN して、「顧客×注文」の世界になる
さらに order_items を JOIN して、「顧客×注文×明細」の世界になる
最後に products を JOIN して、「顧客×注文×明細×商品」の世界になる
この時点で、
1行が「ある顧客の、ある注文の、ある明細の、ある商品」
を表す状態になっています。
ここまで理解できれば、
あとは「それをどう GROUP BY して、何を SUM しているか」
という話に進めます。
GROUP BY が複雑に見える理由
「“何を1行とみなすか”がぼやけているから」
JOIN でテーブルをつないだあと、
GROUP BY が入ると一気に難しく感じます。
でも、GROUP BY がやっていることは一つだけです。
「この単位で、行をまとめてください」と宣言している
さっきのクエリをもう一度見ます。
SELECT
c.name,
p.name,
SUM(oi.quantity * oi.unit_price) AS total_amount
FROM customers c
JOIN orders o
ON c.id = o.customer_id
JOIN order_items oi
ON o.id = oi.order_id
JOIN products p
ON oi.product_id = p.id
GROUP BY
c.name,
p.name;
SQLここでは、
顧客名(c.name)
商品名(p.name)
の組み合わせを「1行」とみなして、
その単位で SUM(oi.quantity * oi.unit_price) を計算しています。
つまり、
「顧客×商品ごとの売上合計」
という意味になります。
GROUP BY を読むときは、
「このクエリは、何を1行として見たいのか?」
と自分に問いかけると、理解が一気に進みます。
“顧客×商品”の売上を、頭の中で追ってみる
「SQL の結果を、自分の言葉で説明できるか」
さっきのクエリを、もう少し丁寧に追ってみます。
SELECT
c.name,
p.name,
SUM(oi.quantity * oi.unit_price) AS total_amount
FROM customers c
JOIN orders o
ON c.id = o.customer_id
JOIN order_items oi
ON o.id = oi.order_id
JOIN products p
ON oi.product_id = p.id
GROUP BY
c.name,
p.name;
SQLJOIN の結果、
1行は「顧客×注文×明細×商品」を表しています。
そこから、
顧客名と商品名の組み合わせごとに行をまとめる
そのまとまりごとに、数量×単価を全部足す
という処理をしています。
例えば、山田さんがマウスを3回に分けて買っていたら、
JOIN の時点では「山田×マウス」の行が3行あります。
GROUP BY で「山田×マウス」にまとめられ、
SUM で3行分の金額が合計されて、
最終的に「山田×マウス」の1行だけが残る、というイメージです。
この「JOIN の結果 → GROUP BY でまとめる → 集計関数で数字を出す」という流れを
自分の言葉で説明できるようになると、
複雑なクエリも“黒魔術”ではなくなります。
複雑なクエリを読むときの“3ステップ”
「FROM〜JOIN → GROUP BY → SELECT の順で見る」
多くの人は、クエリを上から順に読みます。
でも、理解するときは順番を変えた方が楽です。
おすすめは、この順番です。
FROM〜JOIN を読む
どのテーブルがどうつながっていて、1行が何を表しているかをイメージする
GROUP BY を読む
「何を1行としてまとめ直すのか」を確認する
SELECT を読む
その1行ごとに、どんな値(名前・合計・件数など)を出しているかを見る
さっきのクエリに当てはめると、こうなります。
FROM〜JOIN
customers → orders → order_items → products
1行は「顧客×注文×明細×商品」
GROUP BY
c.name, p.name
「顧客名×商品名」を1行とみなす
SELECT
その1行ごとに、顧客名・商品名・売上合計を表示
この順番で読む癖をつけると、
JOIN が増えても、GROUP BY が増えても、
「何をやっているクエリか」を説明しやすくなります。
セキュリティ・監査の視点から見た“複雑クエリ”
「“何をどう集計しているか”を説明できることが、信頼につながる」
売上や顧客データを扱うクエリは、
数字がそのままレポートや意思決定に使われます。
だからこそ、
このクエリは、どのテーブルを使っているのか
どの状態のデータ(status)を含めているのか
どの期間のデータ(ordered_at)を対象にしているのか
何を単位に GROUP BY しているのか
を、自分の口で説明できることが重要です。
「なんか JOIN と GROUP BY を頑張って書いたら、
それっぽい数字が出ました」
では、監査やトラブル時にまったく通用しません。
Day27 でやっているのは、
単に“複雑な SQL を書く練習”ではなく、
「この数字は、こういう前提・こういう切り口で集計したものです」
と説明できる人になるための練習でもあります。
Day27 前半のまとめ
複雑な JOIN が怖く感じるのは、一気に全部を理解しようとするからで、FROM〜JOIN を「テーブル同士を線でつなぐ作業」として1本ずつたどると、必ず意味が追える。
JOIN の結果、1行が「何×何×何…」を表しているのかをイメージしたうえで、GROUP BY を見て「何を1行としてまとめ直しているのか」を意識すると、集計クエリの意味がクリアになる。
クエリを読むときは、FROM〜JOIN → GROUP BY → SELECT の順で見ると、「どのテーブルをどうつなぎ」「何を単位にまとめ」「どんな値を出しているか」が整理しやすい。
顧客×商品ごとの売上など、一見ゴチャっとしたクエリも、「顧客×注文×明細×商品」という行を作り、それを「顧客×商品」でまとめて金額を足しているだけ、と分解して考えれば怖くない。
複雑なクエリほど、「何をどう集計しているのか」を自分の言葉で説明できることが重要で、それがそのままレポートの信頼性や、セキュリティ・監査の観点での“説明責任”につながる。
後半では、
期間条件・ステータス条件・サブクエリ・ビューなども絡めながら、
「現場でそのまま使えるレベルの複雑クエリ」を一緒に組み立てていきます。
