テーブルの関係を確認する
商品一件に対して注文は複数件作れます。これを一対多の関係と呼びます。orders.product_idがproducts.idを参照します。
注文を一件追加SQL
INSERT INTO orders (id, product_id, quantity, ordered_at)
VALUES (3, 1, 1, '2026-09-02');
SELECT * FROM orders ORDER BY id;実行結果OUTPUT
id product_id quantity ordered_at
-- ---------- -------- ----------
1 1 2 2026-09-01
2 2 3 2026-09-02
3 1 1 2026-09-02商品名を注文テーブルへ毎回保存すると、商品名の変更時に複数行を直す必要があります。識別子で関係付け、必要なときに結合します。
INNER JOINで一致する行を結ぶ
内部結合はONの条件が一致する行だけを返します。別名を付けると、どちらのテーブルの列か短く明示できます。
注文と商品を結合SQL
SELECT o.id AS order_id, p.name,
o.quantity, p.price * o.quantity AS total
FROM orders AS o
INNER JOIN products AS p ON p.id = o.product_id
ORDER BY o.id;実行結果OUTPUT
order_id name quantity total
-------- --------- -------- -----
1 SQL入門書 2 5600
2 ノート 3 900
3 SQL入門書 1 2800結合条件を忘れない 関係のない全組み合わせができると、行数が急増します。実行前に「何と何を同じとして結ぶか」を確認します。
LEFT JOINで左側の行を残す
注文のない商品も一覧へ出すには、商品を左側にした外部結合を使います。一致する注文がなければ、注文側の列はNULLになります。
全商品と注文番号SQL
SELECT p.name, o.id AS order_id
FROM products AS p
LEFT JOIN orders AS o ON o.product_id = p.id
ORDER BY p.id, o.id;実行結果OUTPUT
name order_id
---------- --------
SQL入門書 1
SQL入門書 3
ノート 2
ボールペン
マグカップSQL入門書は注文が2件あるため2行になります。外部結合後にWHERE o.id IS NOT NULLとすると注文なしの商品が消え、実質的に内部結合と同じ結果になる点に注意します。
一対多の結果を商品ごとに集計する
商品一行にまとめたい場合は、商品でグループ化します。COUNT(o.id)はNULLを数えないため、注文のない商品は0件になります。
商品別の注文回数と数量SQL
SELECT p.id, p.name,
COUNT(o.id) AS order_count,
COALESCE(SUM(o.quantity), 0) AS total_quantity
FROM products AS p
LEFT JOIN orders AS o ON o.product_id = p.id
GROUP BY p.id, p.name
ORDER BY p.id;実行結果OUTPUT
id name order_count total_quantity
-- ---------- ----------- --------------
1 SQL入門書 2 3
2 ノート 1 3
3 ボールペン 0 0
4 マグカップ 0 0副問い合わせの結果を条件に使う
副問い合わせはSQLの中に置く別の問い合わせです。まず内側で平均価格を求め、その値より高い商品を外側で検索します。
平均より高い商品SQL
SELECT name, price
FROM products
WHERE price > (SELECT AVG(price) FROM products)
ORDER BY price DESC;実行結果OUTPUT
name price
--------- -----
SQL入門書 2800内側が複数行を返す場合は、=ではなくINなど目的に合う演算子が必要です。副問い合わせだけを先に実行し、行数と列を確認すると組み立てやすくなります。
EXISTSで関連行の有無を調べる
EXISTSは副問い合わせが一行でも返せば真になります。次は注文されたことがある商品だけを返します。
注文が存在する商品SQL
SELECT p.name
FROM products AS p
WHERE EXISTS (
SELECT 1
FROM orders AS o
WHERE o.product_id = p.id
)
ORDER BY p.id;実行結果OUTPUT
name
---------
SQL入門書
ノートNOT EXISTSにすると注文のない商品を探せます。NULLを含む可能性があるNOT INより意図を安全に表せる場合があります。
ミニ課題:売上金額を集計する
注文のある商品について、商品名と売上金額(価格×注文数量の合計)を大きい順で表示してください。
解答例SQL
SELECT p.name, SUM(p.price * o.quantity) AS sales
FROM products AS p
INNER JOIN orders AS o ON o.product_id = p.id
GROUP BY p.id, p.name
ORDER BY sales DESC, p.id;実行結果OUTPUT
name sales
--------- -----
SQL入門書 8400
ノート 900