発端
業務でSQL文を組む機会が時々あるのだが、ある時「抽出するユーザーのメールアドレスから自社内のものを除く」という条件を含むSQL文を書くことになり、取り急ぎ書いたのであった。抽出すべきユーザーのメールアドレスは(ユーザーアカウントがあればレコードが生成されていると見なして良い)二つのテーブルにあったため、簡単に書くと次のような文になった。
SELECT user_main.mailaddress, user_additional.mailaddress
FROM user_main
JOIN user_additional
ON user_main.id = user_additional.userid
WHERE user_main.mailaddress NOT LIKE '%@mycorporation.%'
AND user_additional.mailaddress NOT LIKE '%@mycorporation.%';
しかしSQL文をレビューしてもらう最中、「user_additional.mailaddressがNULL
の時があって、その時はuser_main
にレコードがあっても抽出されない」と指摘を受け、実際にそうらしかったので、コードを修正することになった。
-- ... WHERE user_main.mailaddress NOT LIKE '%@mycorporation.%'
AND (
user_additional.mailaddress IS NULL
OR user_additional.mailaddress NOT LIKE '%@mycorporation.%'
);
調べてみた
後になって実際にはどのような処理の影響でこうなっていたのかが気になったので、以下のようなSQL文を実行してみた。
SELECT ('a' LIKE '%'); -- true
SELECT ('a' NOT LIKE '%'); -- false
SELECT (NULL LIKE '%'); -- null
SELECT (NULL NOT LIKE '%'); -- null
以上のように、null
に対してLIKE
句を使うと必ずnull
になることが分かった。なので、最初のコードのようにこれらをAND
で連結している文では、NULL
がレコードに入っていると抽出対象から外れてしまう。