#01 SQLをちゃんと理解する

SELECT文が実行される順番

書く順番と実行される順番は違う

SQLを書くとき、私たちは自然と次のような順番で記述します。

SELECT   name, AVG(score) AS avg_score
FROM     students
WHERE    grade = 3
GROUP BY name
HAVING   AVG(score) >= 70
ORDER BY avg_score DESC
LIMIT    10;

しかしデータベースエンジンがこのクエリを実行する順番は、書いた順番とは異なります。

実行順序を正しく把握していないと「なぜエラーになるのか」「なぜ意図した結果が返らないのか」が理解できません。このエピソードでは実行順序を徹底的に理解しましょう。


実行順序の全体像

SELECT文? テーブルからデータを取得するSQL文。`SELECT * FROM users` のように使う。WHERE で絞り込み、ORDER BY で並び替え、LIMIT で件数を制限できる。 文の実行順序は以下のとおりです。

SELECT文の評価順序 ① FROM テーブル特定 ② JOIN テーブル結合 ③ WHERE 行フィルタ ④ GROUP BY グループ化 ⑤ HAVING グループ絞込 ⑥ SELECT 列の選択 ⑦ DISTINCT 重複除去 ⑧ ORDER BY 並び替え ⑨ LIMIT 行数制限 書く順番(SQL文の順序) SELECT ← ⑥ FROM ← ① WHERE ← ③ GROUP BY ← ④ ORDER BY ← ⑧ エイリアスのスコープ SELECTで定義したエイリアスは WHERE / HAVING では使えない → ORDER BY では使える(⑧だから)  (実行順序がSELECTの後のため)
図1: SELECT文の評価順序(FROM が最初、LIMIT が最後)
ステップ処理内容
1FROM対象テーブルを特定
2JOINテーブルを結合
3WHERE行レベルのフィルタリング
4GROUP BY行をグループ化
5HAVINGグループレベルのフィルタリング
6SELECT列の選択・式の評価
7DISTINCT重複行の除去
8ORDER BY結果の並び替え
9LIMIT行数の制限

各ステップの詳細

FROM と JOIN

最初に実行されるのは FROM 句です。どのテーブルを対象にするかを決めます。複数テーブルがある場合は JOIN が続き、結合した仮想テーブルが生成されます。

-- この時点では全列・全行が対象
FROM orders
JOIN customers ON orders.customer_id = customers.id

WHERE

次に WHERE句? SELECT・UPDATE・DELETE文で行を絞り込む条件を指定する部分。`WHERE age >= 20 AND status = 'active'` のように複数条件を AND / OR で組み合わせられる。 句が適用されます。FROM/JOINで作られた仮想テーブルの各行に対して条件を評価し、条件を満たさない行を除去します。

WHERE orders.created_at >= '2025-01-01'

ポイント: WHERE が実行される時点ではまだ SELECT が評価されていません。つまり SELECT で定義したエイリアスは WHERE では使えません。

GROUP BY

GROUP BY? 指定した列の値が同じ行をまとめてグループ化する句。COUNT・SUM・AVGなどの集計関数と組み合わせて使う。「カテゴリ別の件数を集計」などに使用する。 句によって行がグループ化されます。同じ値を持つ行をひとまとめにし、集約関数(SUM、COUNT、AVGなど)の計算対象となるグループを作ります。

GROUP BY customer_id

HAVING

HAVING句? GROUP BY後のグループに対して絞り込み条件を適用する句。`HAVING COUNT(*) >= 5` のように集計結果でフィルタする。行レベルの絞り込みはWHERE、グループへの絞り込みはHAVING。 句はグループ化された後の結果にフィルターをかけます。WHERE とは異なり、 集計関数? グループや全行に対して計算を行う関数。COUNT(件数)・SUM(合計)・AVG(平均)・MAX(最大)・MIN(最小)がある。GROUP BY と組み合わせて使うことが多い。 関数の結果を条件に使えます。

HAVING COUNT(orders.id) >= 3

SELECT

ここで初めて SELECT 句が評価されます。列の選択、計算式の評価、エイリアスの定義が行われます。

SELECT customer_id, COUNT(*) AS order_count

ORDER BY と LIMIT

ORDER BY? クエリ結果を特定の列で並び替える句。ASC(昇順・デフォルト)またはDESC(降順)を指定できる。複数の列を指定して優先順位をつけることも可能。 は SELECT の後に実行されます。SELECT で定義したエイリアスを ORDER BY で使えるのはこのためです。最後に LIMIT? クエリ結果の取得件数を制限する句。`LIMIT 10` で最初の10件のみ取得する。OFFSET と組み合わせてページネーションを実装できる。 で行数を絞り込みます。


なぜエイリアスを WHERE で使えないか

よくある疑問として「SELECTで定義したエイリアスをWHEREで使えないのはなぜ?」というものがあります。

-- エラーになるクエリ
SELECT price * 1.1 AS price_with_tax
FROM products
WHERE price_with_tax > 1000;  -- エラー: price_with_tax は未定義

実行順序を見れば一目瞭然です。WHERE(ステップ3)の時点では SELECT(ステップ6)はまだ実行されていないため、エイリアス price_with_tax は存在していないのです。

エイリアスが使える場所・使えない場所 実行順 ① FROM / JOIN ③ WHERE ④ GROUP BY ⑤ HAVING ⑥ SELECT 定義 ⑧ ORDER BY alias が存在する範囲 ORDER BY では使用可能 (SELECT の後に実行されるため) alias は未定義 WHERE では使用不可 GROUP BY では使用不可 HAVING では使用不可 (SELECT より先に実行されるため) ❌ エラーになる例 SELECT price * 1.1 AS tax_price WHERE tax_price > 1000; -- NG! ✓ 正しい書き方 SELECT price * 1.1 AS tax_price FROM products ORDER BY tax_price DESC; -- OK!
図2: エイリアスが参照できるのはSELECT以降の処理のみ

回避策は2つあります。

方法1: 式を直接書く

SELECT price * 1.1 AS price_with_tax
FROM products
WHERE price * 1.1 > 1000;  -- 式を繰り返す

方法2: サブクエリを使う

SELECT *
FROM (
  SELECT price * 1.1 AS price_with_tax
  FROM products
) AS t
WHERE price_with_tax > 1000;  -- ここは SELECT の後なので使える

ORDER BY だけはエイリアスを使える

ORDER BY はステップ8で実行されるため、SELECT で定義したエイリアスを参照できます。

SELECT name, price * 1.1 AS price_with_tax
FROM products
ORDER BY price_with_tax DESC;  -- これは OK

MySQLやPostgreSQLなど多くのDBでは、GROUP BY でも SELECT のエイリアスを使える場合がありますが、これは標準SQLの仕様ではなく、DBによる独自の拡張です。移植性を考えると式を直接書くのが安全です。


実行順序を意識したクエリの書き方

実行順序を理解すると、クエリの設計方針が変わります。

パフォーマンスへの影響

WHERE でできるだけ早く行を絞り込むことが重要です。GROUP BY の前に WHERE でフィルタリングすれば、集約処理の対象となる行数を減らせます。

-- 悪い例: 全行を集約してからフィルタ
SELECT customer_id, COUNT(*) AS cnt
FROM orders
GROUP BY customer_id
HAVING order_status = 'completed';  -- HASVINGで行のフィルタはNG

-- 良い例: 先にWHEREで絞り込む
SELECT customer_id, COUNT(*) AS cnt
FROM orders
WHERE order_status = 'completed'  -- 先に絞る
GROUP BY customer_id;

※ HAVING は集約後の条件(COUNT(*) >= 5 など)に使うべきものです。行レベルの条件は必ず WHERE で書きましょう。

デバッグへの活用

クエリがエラーを返したとき、「今はどのステップで失敗しているか?」を実行順序に照らし合わせて考えると原因を素早く特定できます。


まとめ

  • SELECT文の実行順序は FROM → JOIN → WHERE → GROUP BY → HAVING → SELECT → DISTINCT → ORDER BY → LIMIT
  • 書く順番と実行順番は異なる
  • SELECT のエイリアスは WHERE では使えないが ORDER BY では使える
  • WHERE でグループ前に行を絞り込むとパフォーマンスが向上する

次回は JOIN? 複数のテーブルを結合して一つの結果セットにする操作。INNER JOIN(両方に存在する行のみ)・LEFT JOIN(左テーブルの全行 + 一致した右テーブル)などがある。 の内部アルゴリズムと各結合タイプの違いを深掘りします。