NULLの扱い方完全ガイド(IS NULL・COALESCE・3値論理)
NULLとは何か
NULLは「0」でも「空文字」でもなく、「値が存在しない・不明である」ことを表す特別な状態です。SQLを学び始めた人がもっともつまずきやすい概念のひとつで、直感に反する挙動が多いため、きちんと理解しておく必要があります。
-- 電話番号が未入力のユーザー
SELECT name, phone FROM users WHERE phone IS NULL;なぜ「NULL = NULL」はTRUEにならないのか
SQL初心者が最初に驚くポイントがこれです。次のクエリを実行しても、phoneがNULLの行は1件も返ってきません。
-- ❌ これではNULLの行を取得できない
SELECT * FROM users WHERE phone = NULL;これはSQLが「不明 = 不明」を評価するとき、「両方とも分からないのだから、等しいかどうかも分からない」という3値論理(TRUE / FALSE / UNKNOWN)で動くためです。NULLとの比較は常にUNKNOWN(不明)になり、WHERE句ではUNKNOWNは「条件を満たさない」ものとして扱われます。
正しくNULLを判定する:IS NULL / IS NOT NULL
NULLかどうかを判定するには、=ではなく専用の構文IS NULL / IS NOT NULLを使います。
-- 電話番号が未登録
SELECT * FROM users WHERE phone IS NULL;
-- 電話番号が登録済み
SELECT * FROM users WHERE phone IS NOT NULL;COALESCEでNULLを別の値に置き換える
NULLをそのまま表示したくない場合は、COALESCE関数で代わりの値を指定できます。COALESCEは引数を左から順に見て、最初にNULLでない値を返します。
-- 電話番号が未登録なら「未登録」と表示する
SELECT name, COALESCE(phone, '未登録') AS phone
FROM users;
-- 複数候補から最初に見つかった値を使う
SELECT COALESCE(nickname, name, '名無しさん') AS display_name
FROM users;集計関数とNULL:COUNT(*)とCOUNT(column)の違い
集計関数を使うときもNULLの扱いには注意が必要です。
-- テーブルの全行数(NULLも含めて数える)
SELECT COUNT(*) FROM users;
-- phoneがNULLでない行だけを数える
SELECT COUNT(phone) FROM users;COUNT(*)は行そのものの数を数えるのに対し、COUNT(カラム名)はそのカラムがNULLでない値の数だけを数えます。この違いを知らないと、集計結果が想定と合わずに混乱する原因になります。同様にSUM・AVGもNULLの行は計算対象から自動的に除外されます。
WHERE句のNOT INに潜むNULLの罠
NOT INとNULLの組み合わせは、SQLでもっとも有名な落とし穴のひとつです。
-- サブクエリの結果に1件でもNULLが含まれていると…
SELECT * FROM products
WHERE category NOT IN (SELECT category FROM excluded_categories);
-- excluded_categories.category に NULL が1件でもあると
-- このクエリは0件しか返さなくなる(意図せず全滅する)NOT INのリストにNULLが混ざると、内部的に「不明なものと比較して等しくないと言い切れるか?」という判定がUNKNOWNになり、結果的に行が一切マッチしなくなります。NOT INを使う際は、対象のカラムにNULLが含まれていないか事前に確認するか、NOT EXISTSを使う方が安全です。
ORDER BYでNULLはどこに並ぶか
NULLを含むカラムで並び替えると、データベースによってNULLが先頭にくるか末尾にくるかが異なります。明示的に制御したい場合は次のように書けます。
-- NULLを末尾にまとめる(PostgreSQLの場合)
SELECT * FROM users ORDER BY phone NULLS LAST;
-- NULLを先頭にまとめる
SELECT * FROM users ORDER BY phone NULLS FIRST;まとめ
- NULLは「不明・存在しない」を表す特別な値で、0や空文字とは別物
NULL = NULLはTRUEにならない(3値論理のため)- NULL判定には
IS NULL/IS NOT NULLを使う COALESCEでNULLを代わりの値に置き換えられるCOUNT(*)とCOUNT(カラム名)は挙動が異なるNOT INにNULLが混ざると結果が0件になる罠がある(NOT EXISTSが安全)ORDER BY ... NULLS LAST/FIRSTでNULLの並び順を制御できる
