SQLの句の順番:書く順と実行される順の違いを図表で整理

書く順と実行される順のずれを知ると、別名エラーや HAVING の迷いがなくなる。

SQLの書く順と評価順。SELECT の別名は WHERE では使えない
SQLの書く順と評価順。SELECT の別名は WHERE では使えない

SQL の句には「書く順」と「実行される順」の 2 つがある

SELECT 文を書き始めたばかりのころにつまずきやすいのが、句(キーワードで始まるかたまり)の順番です。SQL には、人が書くときに守らなければならない「書く順」と、データベースが意味を解釈するときの「評価される順」(論理的な処理順)の 2 種類があります。この 2 つは一致しません。書く順を間違えると構文エラーになり、評価される順を知らないと「なぜこの別名が使えないのか」「なぜ WHERE に COUNT を書くとエラーになるのか」が理解できません。

なお「評価される順」は、結果がどう決まるかを説明するための論理的な順番です。実際のデータベースは、インデックスの利用や条件の先回りなど、結果が変わらない範囲で内部の処理を最適化します。ここでは結果を正しく予想するための考え方として扱います。

句書く順評価される順役割
WITH0(最初)0(最初)名前付きの一時的な結果(CTE)を定義する
SELECT15出力する列や式、別名を決める
FROM / JOIN21どのテーブルから読むか、どう結合するか
WHERE32行を条件で絞り込む(集計の前)
GROUP BY43行をグループにまとめる
HAVING54グループを集計結果で絞り込む(集計の後)
ORDER BY66結果を並べ替える
LIMIT / OFFSET77先頭から何件を返すか、何件読み飛ばすか

覚え方としては、書く順は「SELECT が先頭、あとは FROM から下へ流れる」、評価される順は「FROM から始まり、SELECT は集計が終わったあと」と押さえると整理しやすくなります。DISTINCT は SELECT の直後に書き、SELECT の評価と同じ段階で重複が除かれます。

書く順:SELECT から LIMIT まで

書く順は文法で決まっています。どれか 1 つでも順番を入れ替えると、多くのデータベースは構文エラー(syntax error)を返します。特に間違えやすいのは、ORDER BY と LIMIT の前後、WHERE と GROUP BY の前後、そして既存の SQL に条件を後から足すときの位置です。

WITH active_users AS (  SELECT id, dept FROM users WHERE active = 1)SELECT dept, COUNT(*) AS nFROM active_usersWHERE dept <> 'temp'GROUP BY deptHAVING COUNT(*) >= 2ORDER BY n DESCLIMIT 10 OFFSET 0;
書く順のひな形(使わない句は省略できる)

条件や件数を後から足すときの位置

すでに動いている SQL に条件や件数制限を足すときは、末尾にそのまま付け足すと順番が崩れがちです。WHERE の条件は ORDER BY より前、LIMIT は ORDER BY より後に入れます。

SELECT name FROM users WHERE active = 1 ORDER BY name AND age >= 20;SELECT name FROM users LIMIT 3 ORDER BY name;
悪い例:ORDER BY の後ろに AND を、ORDER BY の前に LIMIT を書いている
SELECT name FROM users WHERE active = 1 AND age >= 20 ORDER BY name;SELECT name FROM users ORDER BY name LIMIT 3;
良い例:条件は WHERE に、LIMIT は ORDER BY の後に
エラーにならない間違いに注意

1 行目の悪い例は、データベースによってはエラーにならず「name AND age >= 20」という式で並べ替えるだけになり、20 歳未満の行も返ってきます。エラーが出ないことは正しいことの証明になりません。条件を足したら、結果の件数や中身も確かめます。

LIMIT と OFFSET

LIMIT は返す件数、OFFSET は先頭から読み飛ばす件数です。ORDER BY で並べ替えた後に適用されるので、ORDER BY id DESC LIMIT 2 OFFSET 1 なら「id の大きい順で 2 番目と 3 番目」が返ります。ORDER BY を付けずに LIMIT を使うと、どの行が返るかは保証されません。ページ送りなど順番に意味がある場面では、必ず ORDER BY と組み合わせます。LIMIT は PostgreSQL、MySQL、SQLite で使えますが、標準 SQL では FETCH FIRST n ROWS ONLY という書き方もあり、製品によって対応が異なります。

WITH 句は SELECT より前に書く

WITH 句(共通テーブル式、CTE)は、サブクエリに名前を付けて先に定義する書き方です。SELECT より前に書き、定義した名前は後ろの FROM などでテーブルのように使えます。長いサブクエリを入れ子にするより読みやすく、レビューもしやすくなります。

評価される順:FROM から始まり SELECT は後半

データベースは論理的には、まず FROM で対象のテーブル(JOIN があれば結合した結果)を用意し、WHERE で行を絞り、GROUP BY でまとめ、HAVING でグループを絞り、そこで初めて SELECT の列や式を計算します。その後に ORDER BY で並べ替え、最後に LIMIT と OFFSET で件数を切り出します。この順番を知っていると、次の 2 つの疑問に答えられます。

なぜ WHERE で別名が使えないのか

SELECT 句で AS を使って付けた別名(エイリアス)は、SELECT が評価された時点で生まれます。WHERE は SELECT より前に評価されるので、その時点では別名がまだ存在しません。そのため標準 SQL や PostgreSQL では、WHERE に別名を書くと「列が存在しない」というエラーになります。一方、ORDER BY は SELECT より後なので別名を使えます。

SELECT price * qty AS totalFROM order_itemsWHERE total > 1000;
悪い例:WHERE で別名 total を参照している
SELECT price * qty AS totalFROM order_itemsWHERE price * qty > 1000ORDER BY total DESC;
良い例:WHERE には式をそのまま書く。ORDER BY では別名を使える
製品による違い

MySQL や SQLite など一部の製品は、独自の拡張として WHERE や HAVING で別名を受け付ける場合があります。手元で動いても、別のデータベースへ移すとエラーになることがあるため、移植性を考えるなら式をそのまま書くか、サブクエリや WITH 句で別名を先に確定させます。

WHERE と HAVING の違い

WHERE はグループ化の前に、1 行ずつ条件を判定します。HAVING はグループ化の後に、グループごとの集計結果で条件を判定します。COUNT(*) のような集約関数は、グループができてからでないと値が決まりません。そのため WHERE COUNT(*) >= 2 はエラーになり、HAVING に書く必要があります。逆に、個々の行の条件(active = 1 など)は WHERE に書くほうが、集計する行がはじめから減るので意図が明確になり、効率の面でも有利なことが多くなります。

-- 悪い例:WHERE に集約関数SELECT dept, COUNT(*) AS n FROM emp WHERE COUNT(*) >= 2 GROUP BY dept;-- 良い例SELECT dept, COUNT(*) AS nFROM empWHERE active = 1GROUP BY deptHAVING COUNT(*) >= 2;
悪い例と良い例:集計結果の条件は HAVING、行の条件は WHERE
観点WHEREHAVING
判定するタイミングGROUP BY の前GROUP BY の後
判定する単位1 行ずつ1 グループずつ
集約関数(COUNT、SUM など)使えない使える
主な用途対象の行を絞る集計結果でグループを絞る

よくある打ち間違いとレビューで見る点

句の順番と並んで多いのが、キーワードの打ち間違いです。FROM を FORM、HAVING を HAVNG、JOIN を JION、LIMIT を LIMT と打つと、データベースは見慣れない単語として扱い、エラーになります。また、WHEREdept のようにキーワードと列名の間のスペースが抜けると、WHEREdept という 1 つの名前として読まれます。列名の間のカンマ抜けは特に厄介で、SELECT name salary は「salary という別名を付けた name 列」と解釈され、エラーにならず 1 列だけが出力されます。

SELECT name FORM emp;SELECT * FROM emp WHEREdept = 'hr';SELECT name salary FROM emp;SELECT name, salary, FROM emp;SELECT dept, COUNT(*) FROM emp GROUP BY dept HAVNG COUNT(*) > 1;
打ち間違いの例(いずれも意図どおりに動かない)

文字列の比較で "b" のようにダブルクォートを使うのも、よくある誤りです。標準 SQL ではダブルクォートは列名などの識別子を囲む記号で、文字列はシングルクォートで囲みます。SQLite のように互換のためダブルクォートを文字列として受け付ける製品もありますが、同名の列があると列として解釈されるため、意図しない結果になることがあります。

SQL を書いたら確かめるチェックリスト

  • 句が SELECT、FROM、WHERE、GROUP BY、HAVING、ORDER BY、LIMIT の順に並んでいるか
  • 後から足した条件が WHERE の中に AND でつながっているか(ORDER BY の後ろに付いていないか)
  • WHERE で SELECT の別名を参照していないか
  • 集約関数の条件が WHERE ではなく HAVING に書かれているか
  • LIMIT を使うときに ORDER BY があり、ORDER BY の後に LIMIT があるか
  • FROM、WHERE、HAVING、JOIN、LIMIT などの綴りと、キーワードの前後のスペース
  • 列名の間のカンマが抜けていないか、最後の列の後ろに余計なカンマがないか
  • 文字列がシングルクォートで囲まれているか

句の順番は、一度表で整理すれば忘れにくい知識です。一方で、打ち間違いやカンマ抜けは知識があっても起きます。書いた SQL を実行する前に声に出すつもりで 1 句ずつ読み、結果の件数と中身を想定と比べる習慣が、見落としを減らします。

問題で確かめる

この記事の内容を、制限時間つきの問題で確かめられます。

参考

よくある質問

SQL の SELECT 文の句を書く順番は?

SELECT、FROM、WHERE、GROUP BY、HAVING、ORDER BY、LIMIT(と OFFSET)の順です。WITH 句を使う場合は SELECT より前に書きます。使わない句は省略できますが、残った句の順番は入れ替えられません。

SQL が実行される順番(評価順)は?

論理的には FROM(JOIN を含む)、WHERE、GROUP BY、HAVING、SELECT、ORDER BY、LIMIT の順です。実際のデータベースは結果が変わらない範囲で最適化しますが、結果を予想するにはこの順番で考えます。

WHERE 句で SELECT の別名が使えないのはなぜ?

別名は SELECT 句の評価時に生まれ、WHERE 句はそれより前に評価されるためです。WHERE には式をそのまま書くか、サブクエリや WITH 句で別名を先に確定させます。ORDER BY は SELECT より後なので別名を使えます。

WHERE と HAVING はどう使い分ける?

個々の行の条件は WHERE、COUNT や SUM など集計結果の条件は HAVING に書きます。WHERE はグループ化の前、HAVING はグループ化の後に判定されます。

LIMIT と ORDER BY はどちらを先に書く?

ORDER BY を先に、LIMIT を後に書きます。LIMIT は並べ替えた後の結果から件数を切り出すため、ORDER BY がないとどの行が返るかは保証されません。