言語(PHP他)SQL
更新日 : 2020年9月1日
投稿日 : 2020年6月25日

WHERE文をどこにかけるか-全体か、JOIN内か?-

WHERE文をどこにかけるか-全体か、JOIN内か?-の画像

こんにちわ、PHPエンジニアのエンジニア婦人(@naho_osada)です。
私はPHPエンジニアとして7年~の経験があります。WordPressは2年半~の経験があります。その他、jQuery、HTML、CSSも使用します。
ここでは主に過去に納品した案件や自サイト運営(エンジニア婦人ノート)で遭遇したことについて書いています。

今回はSQLが複雑になればなるほど、WHERE条件の「かかるところ」を意識しないと結果が異なって大事故になることがあるよ、というお話です。

ここではJOINの中でJOINする、複雑なSQL-相関サブクエリ-の考え方を例に書いていきます。

テーブル構造のサンプル
テーブル構造
staff
department

WHERE文をどこにかけるか

WHERE文を「どこにかけるか」によって取得できる結果が変わることがあります。

例えば

SELECT * FROM staff as stf
LEFT JOIN department as dep ON dep.department_id=stf.department

ここに「WHERE dep.department_name LIKE “%営業%”」を加えたい、「部署に営業の文字列を含むレコードを取得したい」場合、直感的に2パターンできるかなと予想されます。

パターン1:全体にWHERE

SELECT * FROM staff as stf
LEFT JOIN department as dep ON dep.department_id=stf.department
WHERE dep.department_name LIKE "%営業%"
全体にWHEREをかけて取得した結果

LEFT JOIN で連結した結果を元に、部署に「営業」を含むレコードを絞り込んだ結果

を表示しています。

パターン2:JOINにWHERE

SELECT * FROM staff as stf LEFT JOIN department as dep ON dep.department_id=stf.department
 AND dep.department_name LIKE "%営業%"
JOINにWHEREをかけて取得した結果

LEFT JOIN で部署に「営業」を含むレコードを連結した結果

を表示しています。LEFT JOIN なので名前に「営業」を含まない行もNULLで出力されます(INNER JOIN であればこのNULL行は削除されます)。

どちらがいいかは取得したデータをこの後どうしたいかによって変わるのだろうと思います。(今回の簡単な例では右のNULL行がいらなければINNER JOIN しようよという話でもありますね…)

まとめ:JOINにかかる条件はJOINに書いた方が無難

ケースバイケースではありますが、原則

JOINにかかる条件はJOINに入れる

です。ここではパターン2の方です。
同じようなことをやっていますが、上記2つのように結果が予想と異なってしまい、実際に欲しかったデータが正しく取得できない!ということにもなります。
今回は1つのテーブルの連結だけだったのでこの程度で済みますが、もし複数連結して副問い合わせでもあろうものなら…大事故の危険があります(そういったSQLを書かないのが一番ではありますが、書かないとならないことも往々にしてありますので)

今回私の遭遇したケースでは、ここでいうパターン2のような状態でNULLの行も欲しかったのに、最後にWHERE文で絞り込んでしまったので本来ならあるはずのNULL行がなくなり、その結果バグとなってしまいました。

以上、何かの参考になれば幸いです。