【SQLの罠】なぜ「!=」でNULLが消えるのか?3値論理を理解してバグを防ぐ方法
公開日: 2025年11月30日
データベースを扱っていて、こんな不思議な現象に遭遇したことはありませんか?
「ステータスが『完了(Done)』ではないタスクを全部出したい」と思って、以下のクエリを書きました。
SELECT * FROM tasks WHERE status != 'Done';
しかし、結果を見てみると、ステータスがまだ設定されていない(NULLの)タスクまで、きれいさっぱり消えているのです。 「えっ? NULL は Done じゃないんだから、表示されるべきでしょ?」
これこそが、多くのエンジニアをバグの沼に引きずり込む、SQL最大の罠「3値論理(Three-Valued Logic)」です。 今日は、この不可解なNULLの挙動を理解し、正しくデータを扱うための必須テクニックを解説します。
👻 NULLは「空っぽ」ではない。「不明」である
プログラミング言語(PythonやJava)では、nullやNoneは「値がない」という状態を表します。 しかし、SQLにおけるNULLは、意味合いが少し違います。それは「不明(Unknown)」という意味です。
「神のみぞ知る」状態と言ってもいいでしょう。 そのため、SQLでは以下のルールが適用されます。
NULLとの演算は、すべて「不明(NULL)」になる
- 1 + NULL = NULL (1に不明な数を足しても、結果は不明)
- NULL = 0 = NULL (不明な値は0か?分からないから不明)
- NULL != 'Done' = NULL (不明な値はDoneではないか?それも不明)
WHERE句は、条件が「真(TRUE)」になった行だけを抽出します。 NULL != 'Done' の結果は NULL(不明)なので、TRUEではありません。だから、結果から除外されてしまったのです。
🛡️ 解決策1:IS NULL を使う
NULLを判定するには、専用の構文 IS NULL(または IS NOT NULL)を使う必要があります。 先ほどのクエリを、「完了ではない、または、未設定(NULL)のもの」という条件に書き換えます。
SELECT * FROM tasks
WHERE status != 'Done' OR status IS NULL;
これで、NULLのデータも正しく救い出すことができます。
🚀 解決策2:最強関数 COALESCE(コアレス)
しかし、毎回 OR ... IS NULL と書くのは面倒ですよね。 そこで登場するのが、COALESCE関数です。 これは、「引数の中で、最初に見つかったNULLじゃない値を返す」という関数です。
-- status が NULL なら '未着手' という文字に置き換えて判定する
SELECT * FROM tasks
WHERE COALESCE(status, '未着手') != 'Done';
これなら、NULLは'未着手'という文字列として扱われるため、'Done'との比較が正常に行われ、意図通りの結果が得られます。 計算式で NULL を 0 として扱いたい場合(price + COALESCE(tax, 0))などにも大活躍します。
🤖 AIに、NULL安全なクエリかチェックさせる
複雑な条件分岐や結合(JOIN)を行っていると、どこでNULLが発生し、どこでデータが消えているのかを見抜くのは至難の業です。
そんな時は、AI SQLチェッカーにクエリを貼り付けてみてください。 AIは、「このカラムはNULLになる可能性がありますが、考慮されていますか?」「`COALESCE`を使ったほうが安全です」といったアドバイスをくれます。 人間が見落としがちな「透明な落とし穴」を、AIは見逃しません。
まとめ:NULLを制する者はSQLを制す
- SQLのNULLは「不明」を意味する。
- NULLとの比較や演算は、すべてNULLになり、WHERE句で弾かれる。
- 判定には IS NULL、値の置き換えには COALESCE を使う。
「データが合わない…」というバグの8割は、このNULLの扱いに起因すると言っても過言ではありません。 まずは、自分の書くクエリに COALESCE を使う癖をつけてみましょう。それだけで、あなたのSQLの品質は格段に上がります。
NULLの扱い、自信ありますか?
当サイトの「SQL練習ドリル(道場)」には、NULLを含むデータの集計問題も用意されています。実際に手を動かして、3値論理の挙動を体感してみてください!
SQL道場で練習する →