0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

SQLは書いた順に実行されない ── 評価順を1段階ずつ見ると3つの「なぜ」が同時に解ける

0
Posted at

はじめに

SQL を書き始めた人が、だいたい同じ場所で 3 回つまずきます。

  • WHERECOUNT(*) を書いたらエラーになった
  • GROUP BY していない列を SELECT に書いたらエラーになった
  • なのに ORDER BY では SELECT で付けた別名が使える

3 つとも「そういうものだ」と覚えている人が多いのですが、実はたった 1 つの原理から全部説明がつきます。それは、SQL は書いた順に実行されないということです。

この記事では、その評価順を追いながら 3 つの「なぜ」を一度に潰します。

途中経過は SQL 実行シミュレーター で 1 段階ずつ表示できるようにしてあります。無料・登録不要で、記事に出てくる SQL はそのまま開いて動かせるので、読みながら試してみてください。


SQL は SELECT から書くのに、SELECT は最後に評価される

私たちが SQL を書くときの順番はこうです。

SELECT   -- 1番目に書く
FROM     -- 2番目に書く
WHERE    -- 3番目に書く
GROUP BY -- 4番目に書く
HAVING   -- 5番目に書く
ORDER BY -- 6番目に書く

ところが、データベースが評価する順番はこうです。

その時点で何が起きるか
1 FROM 対象の表を用意する。複数書けば組み合わせ(直積)を作る
2 WHERE 1 行ずつ評価して、条件が真の行だけ残す
3 GROUP BY 指定した列の値が等しい行を 1 つのグループにまとめる
4 HAVING グループ単位で評価して、条件が真のグループだけ残す
5 SELECT 列を選ぶ。ここで初めて別名(AS)が確定する
6 ORDER BY 並べ替える

文の先頭に書く SELECT が、評価では 5 番目です。並べ替えの ORDER BY を除けば、実質いちばん最後に動きます。

この 1 枚の表を頭に入れるだけで、冒頭の 3 つが全部説明できます。順に見ていきます。


なぜ 1: WHERE に集約関数を書けないのか

COUNT(*)AVG(...) のような集約関数は、複数の行を 1 つの値にまとめる関数です。まとめる対象、つまりグループが存在しないと計算できません。

そして評価順を見ると、WHERE2 番目GROUP BY3 番目です。

-- エラーになる
SELECT 部門コード
FROM 従業員
WHERE COUNT(*) >= 2
GROUP BY 部門コード

WHERE が動く時点では、まだグループが 1 つも作られていません。数えるものが無いのに COUNT(*) を求められている状態なので、エラーになります。

グループに対する条件は、グループができた後の HAVING に書きます。

SELECT 部門コード, COUNT(*)
FROM 従業員
GROUP BY 部門コード
HAVING COUNT(*) >= 2
部門コード | COUNT(*)
D01 | 3
D02 | 2
D03 | 2

WHEREHAVING はどう使い分けるのか」という質問はよくありますが、覚えることではありません。行に対する条件なら WHERE、グループに対する条件なら HAVING。評価順のどこにいるかで自動的に決まります。

なお、WHEREHAVING は排他ではなく両方書けます。この場合、先に WHERE で行を捨ててから、残った行だけでグループを作ります。

SELECT 部門コード, COUNT(*)
FROM 従業員
WHERE 給与 >= 300000
GROUP BY 部門コード
部門コード | COUNT(*)
D01 | 2
D02 | 2
D03 | 1

先ほどの HAVING の例では D01 が 3 人でしたが、こちらでは 2 人です。WHERE で先に 1 人落ちてから数えているからで、順番の違いがそのまま結果の違いになっています。


なぜ 2: GROUP BY に無い列を取り出せないのか

これもエラーになります。

-- エラーになる
SELECT 部門コード, 氏名, COUNT(*)
FROM 従業員
GROUP BY 部門コード

理由は「文法違反だから」ではなく、答えが決まらないからです。

GROUP BY 部門コード を評価した時点で、D01 の 3 行は 1 つのグループに畳まれています。グループはもう 1 行として扱われるのに、氏名 は元の 3 行それぞれが別の値を持っている。青木・井上・上田のどれを出せばいいのか、SQL には決めようがありません。

COUNT(*) が書けるのは、3 行を 1 つの値(3)にまとめる方法が定義されているからです。氏名 にはその定義がありません。

つまり GROUP BY の後に書ける列は、次の 2 種類だけです。

  • GROUP BY に指定した列(グループ内で必ず同じ値なので 1 つに決まる)
  • 集約関数で包んだ列(複数行を 1 つにまとめる方法が指定されている)

どうしても氏名が欲しいなら、MAX(氏名) のように「どれを選ぶか」を明示するか、そもそもグループ化せずに 1 行ずつ出すことになります。


なぜ 3: ORDER BY でだけ別名が使えるのか

ここまでは「できない」話でしたが、今度は逆です。

SELECT 部門コード, COUNT(*) AS 人数
FROM 従業員
GROUP BY 部門コード
ORDER BY 人数 DESC
部門コード | 人数
D01 | 3
D02 | 2
D03 | 2

人数 という列は元の表に存在しません。SELECT の中で AS を使って初めて生まれた名前です。それを ORDER BY が参照できています。

評価順を見れば当たり前で、ORDER BYSELECT の後だからです。SELECT が評価された時点で 人数 という列名が確定しているので、その後ろにいる ORDER BY からは見えます。

逆に、同じことを WHERE でやると失敗します。

-- エラーになる
SELECT 給与 AS 月給
FROM 従業員
WHERE 月給 >= 300000

WHERESELECT よりにいるので、月給 という名前がまだ存在しません。「列がありません」と言われます。

別名が使えるかどうかは、SELECT より後ろにいるかどうかで決まる。 これも覚える必要はなく、表を見れば分かります。


途中経過が見えないから難しい

ここまで説明してきて言うのもなんですが、この手の説明を読んで分かった気になるのと、実際に追えるようになるのは別物です。

理由ははっきりしていて、SQL は途中経過を見せてくれないからです。FROM で行が増え、WHERE で減り、GROUP BY で畳まれ、SELECT で列が絞られる ── この過程は全部エンジンの中で起きていて、私たちが受け取るのは最後の結果表 1 枚だけです。

途中が見えないので、結果が想像と違ったときにどこでズレたのかが分からない。「なんとなく合わない」で終わってしまいます。

なので、各段階の表を 1 つずつ表示するツールを作りました。

SQL 実行シミュレーター

書いた SQL に対して FROMWHEREGROUP BYHAVINGSELECTORDER BY の各段階の表が出ます。「一つ進める」を押していくと、どこで行が減り、どこで列が減ったのかが目で追えます。

環境構築もアカウント登録も要りません。処理はすべてブラウザ内で完結します。

たとえば結合の例です。

SELECT 商品.商品名, 在庫.倉庫, 在庫.在庫数
FROM 商品, 在庫
WHERE 商品.商品番号 = 在庫.商品番号
  AND 在庫.在庫数 > 0
商品.商品名 | 在庫.倉庫 | 在庫.在庫数
ボールペン | W1 | 30
ボールペン | W2 | 10
消しゴム | W2 | 25

結果は 3 行ですが、FROM の段階では 5 行 × 4 行 = 20 行あります。そこから結合条件で 4 行に、在庫数の条件で 3 行に絞られています。この「20 行あった」が見えると、結合条件を書き忘れたときに行数が爆発する理由が体感として分かります。

エラーもわざと出せるようにしてあります。「なぜ 1」「なぜ 2」で挙げたエラーは、実際に書くと評価順のどこで詰まったのかまで表示されます。


基本情報技術者試験ではどう問われるか

基本情報技術者試験の科目 A では、データベース分野から 60 問中およそ 4〜6 問が出ます。そのうち SQL を読ませる問題は 1〜3 問で、評価順はその土台になります。出題のされ方は主に 3 つです。

  1. SQL を読んで実行結果を選ぶ。 結合と WHERE を組み合わせて、何行返るかを問う形が多いです
  2. 空欄に入る句を選ぶ。 WHEREHAVING かを選ばせる問題は評価順そのものを問うています
  3. 関係代数の用語と対応させる。 行を選ぶ WHERE が「選択」、列を選ぶ SELECT が「射影」です

シラバス Ver.9.2 の中分類「データ操作」の範囲は、SELECT の各句のほかに、結合・集約関数・副問合せ・集合演算・INSERT / UPDATE / DELETECREATE TABLE と 4 つの制約・ビュー・GRANT・カーソルまで含みます。

この範囲を 12 テーマに分けた解説と、実行結果を当てる 4 択問題を用意しています。

  • 基本情報の SQL レッスン 12 本 ── 各節に実行できるエディタを埋め込んであるので、読んだ直後に試せます
  • 基本情報 SQL 練習問題 14 問 ── すべてオリジナル問題です(IPA 公式の過去問は転載していません)。解答キーは全問エンジンに実行させて検証しているので、解説と実際の出力が食い違うことはありません

まとめ

覚えることは、結局この 1 行だけです。

FROM → WHERE → GROUP BY → HAVING → SELECT → ORDER BY
  • WHERE に集約関数を書けないのは、WHERE(2 番目)の時点でグループ(3 番目)がまだ無いから
  • GROUP BY に無い列を取り出せないのは、グループに畳まれた複数行のどれを出すか決まらないから
  • ORDER BY で別名が使えるのは、ORDER BY(6 番目)が SELECT(5 番目)より後ろにいるから

3 つを個別に暗記していたなら、この 1 行に置き換えてしまうほうが速いです。そして、実際に各段階の表を 1 回見ておくと、この順番は忘れなくなります

SQL 実行シミュレーター


もっと詳しく

基本情報技術者試験 対策ツール

データベースの設計側と、科目 B の対策は別の記事にまとめています。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?