構文解説2026/7/28

SQLのNULLを理解する:欠損値との向き合い方

SQLでデータ分析をしていると、必ずと言っていいほど「NULL」という値に遭遇します。このNULL、一体何者なのでしょうか?単なる空っぽ、それとも特別な意味を持つ値?

最初は「0」や空文字と同じようなものかと思ってしまうかもしれません。しかし、SQLの世界ではNULLは非常に独特な存在です。その扱い方を間違えると、予期せぬ結果を招き、データ分析の精度を大きく下げてしまうこともあります。

この記事では、SQLにおけるNULLの基本的な性質から、具体的なSQL文での挙動、そしてNULLを安全かつ効果的に扱うためのテクニックまでを、分かりやすく解説していきます。

NULLとは何か?:未知の値の表現

まず、NULLの基本的な定義から確認しましょう。

NULLは、値が存在しない、あるいは不明であることを示す特別なマーカーです。

これは、数値の「0」や空文字('')とは全く異なります。

  • 0: 数値としてのゼロという値が存在します。
  • 空文字 (''): 文字列としての空の値が存在します。
  • NULL: 値そのものが存在しない、あるいは未知であることを示します。

例えば、顧客テーブルに「年齢」という列があるとします。

ある顧客の年齢が不明な場合、それは「0歳」でも「不明な文字列」でもなく、「NULL」として記録されます。

この「値が存在しない」という性質が、SQLでのNULLの振る舞いをユニークなものにしています。

NULLとの比較:なぜ期待通りに動かないのか?

NULLの最も特徴的な性質は、比較演算子 (=, !=, <, > など) との組み合わせで、常に「不明」という結果を返すことです。

これは、NULLが「値が存在しない」ことを示すため、他の値と比較しようとしても、その比較対象となる値自体が存在しないからです。

例えば、以下のようなテーブルがあるとします。

CREATE TABLE users (
  id INTEGER PRIMARY KEY,
  name TEXT,
  age INTEGER
);

INSERT INTO users (name, age) VALUES
('Alice', 30),
('Bob', NULL),
('Charlie', 25);

このテーブルで、「年齢が30歳の人」を検索しようとした場合、WHERE age = 30 と記述します。

SELECT name FROM users WHERE age = 30;

このクエリは、Aliceのみを返します。

では、「年齢がNULLの人」を検索しようとしたらどうなるでしょうか?

WHERE age = NULL と書きたくなりますが、これは期待通りに動きません。

SELECT name FROM users WHERE age = NULL;

このクエリは、何も返しません

なぜなら、NULL = NULL という比較は、真偽値(TRUE/FALSE)ではなく、「不明」(UNKNOWN)という結果になるからです。

WHERE句は、条件がTRUEになった行のみを抽出するため、UNKNOWNは結果として扱われません。

同様に、WHERE age != NULL も、NULL以外の値と比較しようとしても、その比較対象が不明なため、常にUNKNOWNとなり、期待通りの結果は得られません。

NULLを安全に扱うための IS NULLIS NOT NULL

NULLとの比較には、専用の演算子 IS NULLIS NOT NULL を使用します。

これらは、値が存在するかどうかを直接判定するため、期待通りの結果を得られます。

「年齢がNULLの人」を検索するには、以下のように記述します。

SELECT name FROM users WHERE age IS NULL;

これにより、Bobが正しく取得できます。

逆に、「年齢がNULLでない人」を検索するには、IS NOT NULL を使用します。

SELECT name FROM users WHERE age IS NOT NULL;

これにより、AliceとCharlieが取得できます。

この IS NULLIS NOT NULL は、NULLを扱う上での基本中の基本です。必ず覚えておきましょう。

NULLと集計関数:期待される挙動と注意点

SQLの集計関数(COUNT, SUM, AVG, MAX, MIN など)は、NULL値をどのように扱うのでしょうか?

多くの場合、集計関数はNULL値を無視して計算を行います。

これは、NULLが「値が存在しない」ことを示すため、計算の対象から除外するのが自然だからです。

例えば、先ほどのusersテーブルで、COUNT(*)COUNT(age) の違いを見てみましょう。

SELECT
  COUNT(*) AS total_rows,
  COUNT(age) AS non_null_ages
FROM users;

このクエリの実行結果は以下のようになります。

total_rowsnon_null_ages
32
  • COUNT(*) は、テーブルの全ての行数をカウントします。そのため、結果は3です。
  • COUNT(age) は、age列がNULLでない行のみをカウントします。Bobの年齢はNULLなので、AliceとCharlieの2行のみがカウントされ、結果は2となります。

SUMAVG も同様に、NULL値を無視して計算します。

SELECT
  SUM(age) AS total_age,
  AVG(age) AS average_age
FROM users;

このクエリの実行結果は以下のようになります。

total_ageaverage_age
5527.5
  • SUM(age) は、30 + 25 = 55 となります。
  • AVG(age) は、(30 + 25) / 2 = 27.5 となります。

BobのNULL値は計算に含まれていません。

注意:AVG 関数と COUNT(column) の関係

AVG(column) は、内部的には SUM(column) / COUNT(column) で計算されることが多いです。そのため、COUNT(column) がNULLを無視するのと同様に、AVG(column) もNULLを無視します。

もし、NULLを0として扱って平均を計算したい場合は、COALESCE 関数などを用いて明示的に変換する必要があります。

MAXMIN もNULL値を無視します。ただし、NULL値が存在する場合、それらの関数はNULL以外の値の中から最大値・最小値を返します。

NULL値がテーブルに存在する場合、集計結果が期待と異なることはよくあります。集計関数がNULLをどのように扱うかを理解しておくことが重要です。

NULLを安全に扱うためのテクニック

NULLの扱いに慣れてくると、その特性を理解し、より安全にSQLを記述できるようになります。

ここでは、いくつかの実践的なテクニックを紹介します。

1. COALESCE 関数:NULLを代替値に置き換える

COALESCE 関数は、引数リストの中から最初に見つかったNULLでない値を返します。

これにより、NULL値を特定のデフォルト値に置き換えることができます。

例えば、先ほどのusersテーブルで、年齢がNULLの場合は「不明」という文字列に置き換えて表示したい場合、以下のように記述します。

SELECT name, COALESCE(age, -1) AS age_display
FROM users;

このクエリの実行結果は以下のようになります。

nameage_display
Alice30
Bob-1
Charlie25

ここでは、NULLだったBobの年齢を-1に置き換えています。

集計関数と組み合わせて使うこともよくあります。例えば、NULLの売上を0として合計したい場合などです。

SELECT SUM(COALESCE(sales, 0)) AS total_sales
FROM orders;

COALESCEは、NULLを扱いたい場面で非常に強力な関数です。

2. NULLIF 関数:特定の値をNULLに置き換える

NULLIF 関数は、2つの引数を比較し、もし2つが等しければNULLを返します。そうでなければ、最初の引数を返します。

これは、特定の値をNULLとして扱いたい場合に便利です。

例えば、注文数量が0の場合、それをNULLとして扱いたい場合などに使えます。

SELECT
  order_id,
  NULLIF(quantity, 0) AS effective_quantity
FROM order_details;

もしquantityが0であれば、effective_quantityはNULLになります。そうでなければ、元のquantityの値が返されます。

3. CASE WHEN 文でのNULL処理

CASE WHEN 文は、条件分岐を行うためのSQL構文ですが、NULLの判定にも柔軟に対応できます。

IS NULLIS NOT NULL と組み合わせて、NULLの場合に特定の処理を行うことができます。

SELECT
  name,
  CASE
    WHEN age IS NULL THEN '年齢不明'
    WHEN age < 20 THEN '若年層'
    ELSE '成人'
  END AS age_group
FROM users;

このクエリは、年齢がNULLの場合は「年齢不明」、20歳未満の場合は「若年層」、それ以外の場合は「成人」と表示します。

4. テーブル設計におけるNULLの検討

そもそも、データベース設計の段階で、どの列にNULLを許容するかを慎重に検討することが重要です。

必須の項目であればNULLを許容しない(NOT NULL制約を付ける)などの対策が考えられます。

しかし、必ずしも全てのデータが常に存在するとは限りません。例えば、オプションの項目や、まだ情報が入力されていない項目などでは、NULLを許容することが適切です。

NULLを許容する際は、その値がどのような意味を持つのかを明確にしておくことが、後々のデータ分析やアプリケーション開発で混乱を防ぐ鍵となります。

まとめ:NULLは「未知」を正しく扱うための鍵

SQLにおけるNULLは、単なる空の値ではなく、「値が存在しない」「未知である」ことを示す特別な概念です。

その比較演算子との独特な挙動や、集計関数での無視される性質を理解することは、正確なデータ分析を行う上で不可欠です。

  • NULLは0や空文字とは異なる。
  • NULLとの比較には IS NULLIS NOT NULL を使う。
  • 集計関数は基本的にNULLを無視する。
  • COALESCENULLIFCASE WHEN を活用してNULLを安全に処理する。

これらの知識を身につけることで、SQLでのデータ操作における多くの「なぜ?」が解消され、より自信を持ってデータと向き合えるようになるはずです。

NULLの扱いに戸惑うことはありません。それは、データが持つ「不確実性」や「未知」を正しく表現するためのSQLの機能なのです。


QSQL Quest