クイズ
以下のクエリを実行すると、何件のレコードが返ってくるでしょうか?
SELECT COUNT(*)
FROM users
WHERE email NOT LIKE 'mask-%';
usersテーブル
| id | |
|---|---|
| 1 | 'kuzuba@example.com' |
| 2 | 'mask-abc' |
| 3 | NULL |
答え: 1件
「NULLはmask-で始まらないから2件じゃないの?」と思った方のために、この記事を書こうと思います。僕も実務でWHERE句を書いていて、ふと思った時があったので簡単にまとめます。
NULLとは何か
値ではなく「状態」
NULLは値が不明である状態であることを表します。
クエリを実行すると以下のように判断されます。
email = 'kuzuba@example.com' --→ 「値がある」と解釈する
email = '' --→ 「値がある(空文字だけど)」と解釈する
email = NULL --→ 「不明(値がないのではなく)」と解釈する
NULLは、値がないのではなく「不明」と解釈される
空文字との違い
空文字とは明確に違いがあるので、まとめておきます。
| 項目 | 空文字 ''
|
NULL |
|---|---|---|
| 意味 | 「空っぽ」という値 | 値が不明 |
| 比較 |
= '' で判定可能 |
IS NULL が必要 |
| 文字数 | 0文字 | 不明 |
なぜNOT LIKEで引っかからないのか
SQLの3値論理
SQLは値が何かを判断する時、
- 1.TRUE
- 2.FALSE
- 3.UNKNOWN(不明)
という3つの状態に分けます。
NULLがくると、箱を開けてみないと何が入っているか分からない、、、だから中身は知らない!という意味でUNKNOWNとします。
具体的には以下のようになります。
NULL = NULL
NULL != NULL
--→ 不明なので一緒かどうかも判定できないのでUNKNOWNと解釈する
NULL > 10
--→ 10より大きいのか判定できないのでUNKNOWNと解釈する
WHERE句の評価ルール
最初のクイズに戻りますが、
WHERE句では、TRUEになったレコードだけを絞り込むので、
WHERE email NOT LIKE 'mask-%'
は以下のように判定します。
'kuzuba@example.com' → TRUE(返す)
'mask-abc' → FALSE(返さない)
NULL → UNKNOWN(返さない)
人間の直感では「NULLはmask-で始まらないんだからTRUEでしょ」と思いたくなりますが、データベースはもっと慎重に判断すると思っておくと良さそうです。
じゃ、クエリ書く時はどうするのか
マスクされていないデータを検出したいとき、NULLのレコードも「マスクされていない」として扱いたい場合があります。
そんなときは、OR email IS NULLをつけて明示的に追加します。
NULLも含めて「mask-」として検出したい時
WHERE (email NOT LIKE 'mask-%' OR email IS NULL)
これで、'kuzuba@example.com'のような通常の値も、NULLも両方検出できます。