こんにちわ、PHPエンジニアのエンジニア婦人(@naho_osada)です。
PHPエンジニアとして9年~の経験があります。
今回はSQLが複雑になればなるほど、WHERE条件の「かかるところ」を意識しないと結果が異なって大事故になることがあるよ、というお話です。
ここではJOINの中でJOINする、複雑なSQL-相関サブクエリ-の考え方を例に書いていきます。



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 "%営業%"

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 "%営業%"

LEFT JOIN で部署に「営業」を含むレコードを連結した結果
を表示しています。LEFT JOIN なので名前に「営業」を含まない行もNULLで出力されます(INNER JOIN であればこのNULL行は削除されます)。
どちらがいいかは取得したデータをこの後どうしたいかによって変わるのだろうと思います。(今回の簡単な例では右のNULL行がいらなければINNER JOIN しようよという話でもありますね…)
まとめ:JOINにかかる条件はJOINに書いた方が無難
ケースバイケースではありますが、原則
JOINにかかる条件はJOINに入れる
です。ここではパターン2の方です。
同じようなことをやっていますが、上記2つのように結果が予想と異なってしまい、実際に欲しかったデータが正しく取得できない!ということにもなります。
今回は1つのテーブルの連結だけだったのでこの程度で済みますが、もし複数連結して副問い合わせでもあろうものなら…大事故の危険があります(そういったSQLを書かないのが一番ではありますが、書かないとならないことも往々にしてありますので)
今回私の遭遇したケースでは、ここでいうパターン2のような状態でNULLの行も欲しかったのに、最後にWHERE文で絞り込んでしまったので本来ならあるはずのNULL行がなくなり、その結果バグとなってしまいました。
以上、何かの参考になれば幸いです。