読者です 読者をやめる 読者になる 読者になる

ぱと隊長日誌

ブログ運用もエンジニアとしての生き方も模索中

SQLのWHERE句の処理を理解する

プログラミング

はじめに

SQLのWHERE句は「集合から条件に合致する行を選択する」処理です。
ですが、SQLの様々な書き方を学ぶうちにWHERE句の役割について混乱してしまうことがあるかもしれません。本エントリではこの混乱の解消を目標に説明します。

SQLの実行順序

SQLの実行順序は以下の通り決まっています。
FROM→WHERE→GROUP BY→HAVING→SELECT

このうち、本エントリで取り上げるのはSELECT, FROM, WHEREです。
SELECTは集合から列を選択します。
FROMは対象とする集合を指定します。
WHEREは集合から条件に合致する行を選択します。

この後の説明を読み進めるときはこの順番を意識してみてください。

WHERE句の評価は1行ずつ行われる

ここにmember表があります。

name sex_code
あきら 1
ゆみこ 2
けんじ 1

これに対して"sex_code=1"を条件にSQLを発行します。

SELECT name
  FROM member
 WHERE sex_code = 1;

このSQLのWHERE句は対象となる集合のmemberに対して1行ずつ評価を行います。
WHERE句の評価の結果として以下の行が選択されました。

name sex_code
あきら 1
けんじ 1

選択リストに従い、"name"列が選択されます。

name
あきら
けんじ

見出しにも挙げましたが、「WHERE句の評価は1行ずつ行われる」というのがポイントです。
あなたがmember表とSQLを見比べて手作業で処理をするとき、member表が小さいこともあり、全体を俯瞰して一度に処理している感覚になるかもしれません(頭の中では1行ずつ処理しているとしても)。ですが、DBは素直に1行ずつ処理を行います。

これは推測ですが(裏付けとなる資料が見つかりませんでした)、「WHERE句の評価は1行ずつ行われる」ことがWHERE句で集約関数を使えないことにつながっているのかもしれません。
WHERE句に集約関数を使えたとしても、WHERE句の評価は1行ずつのため、集約関数に渡される集合の値は1つのみです。これでは集約関数の意味がありません。

例えば、WHERE句にMAX関数を含んだSQLを書けたとします。

SELECT name
  FROM member
 WHERE sex_code = MAX(sex_code);

WHERE句の評価は1行ずつです。member表の先頭行を評価するとします。

name sex_code
あきら 1

この時、MAX関数にsex_code=1が渡されても、それはSQL実装者の意図と異なるでしょう。
WHERE句に集約関数を使えないのは、このような意味のない記述を避けるためだったのかもしれません。

結合条件を含んだ行選択

先ほどのmember表に加え、sex表を用意します。

sex_code sex_jp
1
2
SELECT m.name,
       s.sex_jp
  FROM member m,
       sex s
 WHERE m.sex_code = s.sex_code
   AND m.sex_code = 1;

このSQLでイメージするのはmember表とsex表をsex_code列の値に応じて結合し、その結果からsex_code=1の行を選択することではないでしょうか?
ですが、これは「集合から条件に合致する行を選択する」を超えて、テーブルの結合処理まで行っています。WHERE句しかないのにJOIN句まで指定されたかのようにふるまうのはなぜでしょうか?

そこでまずは選択リスト、WHERE句を除いて考えてみます。

SELECT *
  FROM member m,
       sex s;

この結果はmember表とsex表のクロス結合(CROSS JOIN)となります。

m.name m.sex_code s.sex_code s.sex_jp
あきら 1 1
ゆみこ 2 1
けんじ 1 1
あきら 1 2
ゆみこ 2 2
けんじ 1 2

ここで改めて選択リスト、WHERE句を戻してみます。

SELECT m.name,
       s.sex_jp
  FROM member m,
       sex s
 WHERE m.sex_code = s.sex_code 
   AND m.sex_code = 1;

このWHERE句は以下の条件に合致する行を選択するものです。
「m.sex_code = s.sex_code」かつ「m.sex_code = 1」
先ほどのクロス結合の表からこの条件に合致する行を選択します。

m.name m.sex_code s.sex_code s.sex_jp
あきら 1 1
けんじ 1 1

選択リストで指定しているのはm.name, s.sex_jp列ですので、以下の結果となります。

m.name s.sex_jp
あきら
けんじ

このように処理を追って考えると、WHERE句は「集合から条件に合致する行を選択する」ことに変わりないとわかります。
実際の内部処理はここで示したようなクロス結合を作ることなく行われますが、結果は同様となります。

結合条件と検索条件を区別する

結合条件を含んだWHERE句がわかりづらいと感じるのであれば、標準SQLに則って"JOIN"で書き直すとわかりやすくなります。

SELECT m.name,
       s.sex_jp
  FROM member m,
       sex s
 WHERE m.sex_code = s.sex_code
   AND m.sex_code = 1;

これは"INNER JOIN"で以下のSQLに書き直せます。

SELECT m.name,
       s.sex_jp
  FROM member m INNER JOIN sex s
    ON m.sex_code = s.sex_code
 WHERE m.sex_code = 1;

ここでもまずは選択リスト、WHERE句を除外して考えてみます。

SELECT *
  FROM member m INNER JOIN sex s
    ON m.sex_code = s.sex_code;
m.name m.sex_code s.sex_code s.sex_jp
あきら 1 1
ゆみこ 2 2
けんじ 1 1

選択リスト、WHERE句を戻してみます。

SELECT m.name,
       s.sex_jp
  FROM member m INNER JOIN sex s
    ON m.sex_code = s.sex_code
 WHERE m.sex_code = 1;

WHERE句の"m.sex_code = 1"に合致する行を選択します。

m.name m.sex_code s.sex_code s.sex_jp
あきら 1 1
けんじ 1 1

選択リストで指定しているのはm.name, s.sex_jp列ですので、以下の結果となります。

m.name s.sex_jp
あきら
けんじ

先ほどのクロス結合にしてから考えた場合と同じ結果となりました。

まとめ

SQLを理解し辛く感じた時は実行順序に立ち返り、まずはFROMでどのような集合を対象にしているか考えるとわかりやすいかもしれません。WHEREはその集合に対して行を選択するものです。
WHERE句に結合条件があればテーブルの結合だ、と形から直截に理解することもよいと思うのですが、様々なSQLの書き方を学ぶうちにその表現力の豊かさから混乱してしまう時があるかもしれません。そんなときは今回のように立ち返って見直していただければと思います。

参考資料

達人に学ぶSQL徹底指南書

達人に学ぶ SQL徹底指南書 (CodeZine BOOKS)

達人に学ぶ SQL徹底指南書 (CodeZine BOOKS)

SQLの実行順序及び"JOIN"での書き換えを参考にさせていただきました。また、SQLのフォーマットも本エントリの参考にしています。
この本はSQLを使い始めて半年から一年程度の経験があることを想定しており、なじみのない記述は難しく感じますので、まずは興味のある個所からつまみ読みするのが楽しいと思います。

プログラマのためのSQL 第4版

プログラマのためのSQL 第4版

プログラマのためのSQL 第4版

SQLの教科書といえる本です。著者の序文にある通り、入門書ではなく1年程度の実務経験のある方を対象としています。
ただ、今回のエントリでまとめた基礎レベルの話も解説されています(例えば「24 単純なSELECT文」)。WHERE句とは何か、という一見当たり前のことでも、その概念モデルに立ち戻って確認するのに良い本だと思います。

更新情報

2017/01/28

  • SQLのフォーマットを整えました。
  • 見出しの「単純な行選択」を「WHERE句の評価は1行ずつ行われる」に変更し、内容の見直しを行いました。