はじめに
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)
- 作者: ミック
- 出版社/メーカー: 翔泳社
- 発売日: 2008/02/07
- メディア: 単行本(ソフトカバー)
- 購入: 54人 クリック: 1,004回
- この商品を含むブログ (78件) を見る
SQLの実行順序及び"JOIN"での書き換えを参考にさせていただきました。また、SQLのフォーマットも本エントリの参考にしています。
この本はSQLを使い始めて半年から一年程度の経験があることを想定しており、なじみのない記述は難しく感じますので、まずは興味のある個所からつまみ読みするのが楽しいと思います。
更新情報
2017/01/28
- SQLのフォーマットを整えました。
- 見出しの「単純な行選択」を「WHERE句の評価は1行ずつ行われる」に変更し、内容の見直しを行いました。