【ITニュース解説】NULL in SQL: Why = NULL Finds Nothing and What to Write Instead
2026年08月22日に「Dev.to」が公開したITニュース「NULL in SQL: Why = NULL Finds Nothing and What to Write Instead」について初心者にもわかりやすく解説しています。
ITニュース概要
SQLのNULLは「不明」を意味し、ゼロや空文字とは別物だ。`= NULL`では何も見つからず、`IS NULL`で有無を確認する。`WHERE`は「yes」の行だけを残し、NULL値による「unknown」な行も除外する3値論理の理解が不可欠。集計関数や`NOT IN`でのNULLの挙動に注意し、データ欠損を正しく扱おう。
ITニュース解説
SQLにおけるNULL値の振る舞いを理解することは、データベースを扱う上で極めて重要である。NULLは「値が存在しない」「未知である」ことを意味し、0や空の文字列とは根本的に異なる。この違いを認識していないと、SQLクエリが期待と異なる結果を返したり、データ分析が誤った結論を導いたりする原因となる。
まず、最もよくある誤解は「= NULLでNULL値を持つ行を検索できる」というものだ。例えば、SELECT * FROM tickets WHERE assignee = NULL;というクエリは、担当者(assignee)がNULLのチケットを見つけようとするが、結果は常に0行となる。これは、NULLが未知の値であるため、ある未知の値が別の未知の値と等しいかどうかを比較することはできないからである。NULL = NULLという比較自体も「未知」という結果を返し、「真」(True)にはならない。SQLのWHERE句は、比較の結果が「真」と判断された行のみを抽出する。そのため、assignee = NULLのように比較結果が「未知」となる場合は、該当する行が抽出されない。
では、どのようにしてNULL値を持つ行を検索すればよいのだろうか。SQLにはIS NULLという専用の構文が用意されている。SELECT * FROM tickets WHERE assignee IS NULL;と記述することで、「この行の担当者欄は空か?」という問いに対し、「はい」または「いいえ」という明確な答えを得ることができ、正しくNULL値を持つ行を抽出できる。同様に、NULL値ではない行を検索するにはIS NOT NULLを使用する。これらの構文は、値そのものではなく「値が存在するか否か」という状態をチェックするため、NULLの扱いに特化している。
SQLの比較演算は、通常のプログラミング言語のように「真」か「偽」の二択ではない。SQLでは「真」「偽」「未知」という三値論理が採用されている。NULLが関わる比較はすべて「未知」となり、WHERE句はこの「未知」の行も「偽」の行と同様に結果から除外する。この「YES」の行だけを通すという原則が、NULLに関する様々な問題の根源である。
この三値論理の原則は、<>(不等号)演算子を使う際にも注意が必要だ。例えば、SELECT * FROM tickets WHERE assignee <> 'Priya';というクエリは、「担当者が'Priya'ではないチケット」を抽出するように見える。しかし、担当者カラムにNULL値を持つチケットは、NULL <> 'Priya'という比較も「未知」となるため、WHERE句によって結果から除外されてしまう。もし「Priya以外のすべてのチケット、未割り当てのものも含む」という意味でこのクエリを書いたのであれば、WHERE assignee <> 'Priya' OR assignee IS NULL;のように、明示的にNULL値の行も含める必要がある。
さらに厄介なのがNOT IN句である。SELECT * FROM tickets WHERE assignee NOT IN (SELECT name FROM closers);のようなクエリで、closersテーブルのnameカラムに一つでもNULL値が含まれている場合、このクエリは驚くべきことに0行を返す。NOT IN句は内部的に複数のAND条件に展開されるため、assignee <> 'Priya' AND assignee <> 'Marcus' AND assignee <> NULLのような形になる。このとき、assignee <> NULLという条件が常に「未知」を返すため、ANDで結合された全体の条件も常に「未知」となり、結果的にどの行もWHERE句を通過できなくなるのだ。この問題を避けるには、サブクエリからNULLをフィルタリングするか(SELECT name FROM closers WHERE name IS NOT NULL)、あるいはNOT EXISTS句を使用する方法がある。NOT EXISTSは行ごとにマッチングの有無をチェックするため、NULLの影響を受けにくい。どちらの解決策を選ぶかは、「未割り当てのチケットもリストにないものとみなすか」という、業務上の要件によって変わる。
NULL値を他の値に置き換えて表示したい場合は、COALESCE関数が便利だ。COALESCE(assignee, 'Unassigned')と書けば、assigneeがNULLの場合は'Unassigned'と表示され、そうでない場合はassigneeの元の値が表示される。これはレポートなどでNULLを分かりやすく表示するために有効な手段だが、注意が必要なのは、数値型のNULLを0などに置き換える場合である。例えば、COALESCE(minutes_to_close, 0)としてクローズ時間を0に置き換えると、本来「未知」であったデータが「0分でクローズした」という明確な事実として扱われてしまい、平均値などの統計値が大きく歪む可能性がある。データの欠損を隠蔽するのではなく、適切に表現するべきである。
集計関数もNULLの扱いに注意が必要だ。SUM、AVG、MIN、MAX、COUNT(column)のような集計関数は、デフォルトでNULL値を無視して計算を行う。例えば、AVG(minutes_to_close)は、minutes_to_closeカラムに存在する非NULL値のみを対象に平均を計算するため、対象となる行の総数ではなく、非NULL値の数で割って平均を出す。これに対し、COUNT(*)はテーブルの全行数を数え、COUNT(column)は指定されたカラムの非NULL値の数を数える。この二つのCOUNTの結果を比較することで、そのカラムにどれだけのNULL値(データ欠損)があるかを素早く把握できる。これは、データ品質をチェックする上で非常に有用な方法だ。
さらに、GROUP BY句では、NULL値はすべて一つのグループとして扱われる。これは、GROUP BYが「どの行が一緒に属するか」という問いに答える際、すべての未知の値は一緒に属すると標準で定義されているためである。ORDER BY句でのNULLのソート順は、データベースの種類によって異なる場合がある(一部のデータベースではNULLを最小値として扱うが、別のデータベースでは最大値として扱うなど)。そのため、特定のソート順が必要な場合は、NULLS FIRSTやNULLS LASTといったオプションを明示的に指定することが推奨される。
最後に、空文字列('')とNULLは、ほとんどのデータベースシステムで異なる概念として扱われる。空文字列は「値がないことが既知の状態」だが、NULLは「値が未知の状態」だ。フィルターやカウントではこれらが区別されるため、WHERE col = ''とWHERE col IS NULLは異なる結果を返すことが多い。ただし、Oracleデータベースのように、空文字列をNULLとして扱う特殊なケースも存在するので注意が必要である。
これらのNULLの振る舞いは、一つ一つを暗記するよりも、「未知」という概念と「WHERE句は『真』の行のみを通過させる」というSQLの基本原則を理解することで、多くの現象を予測できるようになるだろう。正確なデータ分析と信頼性の高いシステムを構築するためには、NULLの特性を深く理解し、適切に扱うことが不可欠である。特に、クエリが期待と異なる行数を返した場合、まず対象のカラムにNULLが含まれていないかを確認することが、問題解決への第一歩となるだろう。