【SQL】NULLの扱い方:IS NULL・IS NOT NULLでハマらないために

SQL

こんにちは、かつコーチです。
WHERE column = NULLと書いたのに、なぜか1件もヒットしない」——SQLを書き始めた人がほぼ必ず一度はハマる落とし穴です。
今回は、NULL(値が存在しない、未入力であることを示す特殊な状態)の正しい扱い方と、集計・比較で起こりがちな事故のパターンを解説します。

NULLとは?

NULLは「0」でも「空文字」でもない

NULLは、そのカラムに値が入っていない状態を表す特別なマーカーです。
数値の0や、文字列の空文字('')とは明確に異なります。

  • age = 0:年齢が0歳というデータが入っている
  • age = '':ありえない(数値型に空文字は入らない)
  • age IS NULL:年齢が未入力・不明

例えば、会員テーブルのmiddle_name(ミドルネーム)カラムがNULLなのは「ミドルネームを持っていない」のではなく「そのデータが未入力・不明」という意味です。

なぜ= NULLでは判定できないのか

SQLの世界では、NULLは「不明な値」として扱われます。
「不明な値」同士を=で比較しても、「等しいかどうかも不明」という結果になり、trueにもfalseにもなりません。
そのため、WHERE age = NULLと書いても、常に条件は成立せず、0件がヒットします。

基本の書き方

IS NULL / IS NOT NULLで判定する

NULLかどうかを判定するには、専用のIS NULLIS NOT NULLを使います。

❌ Before(=ではNULLを判定できない)

SELECT name FROM users WHERE middle_name = NULL;
-- 常に0件(意図と違う)

✅ After(IS NULLを使う)

SELECT name FROM users WHERE middle_name IS NULL;

逆に、値が入っている行だけを取りたい場合はIS NOT NULLを使います。

SELECT name, phone_number
FROM users
WHERE phone_number IS NOT NULL;

電話番号が登録されている会員だけに絞り込む、といった使い方です。

COALESCEでNULLを別の値に置き換える

COALESCE関数を使うと、NULLだった場合に代わりの値を表示できます。

SELECT name, COALESCE(nickname, name) AS display_name
FROM users;

ニックネームが未登録の会員は、代わりに本名を表示する、といった画面表示のロジックをSQL側で実現できます。

つまずきやすい設定・注意点

集計関数はNULLを無視する

COUNTSUMAVGといった集計関数は、対象カラムにNULLがあると、そのNULLの行を計算から除外します。
これを知らずに集計すると、意図しない件数・平均値になることがあります。

私が経験した実例です。
あるECサイトのレビュー機能で、評価点(rating)カラムに、レビュー未評価の注文はNULLが入る設計になっていました。
「平均評価を出してほしい」という依頼でAVG(rating)を使ったところ、未評価の注文が母数から除外され、実際より評価が高く見える集計結果になってしまいました。

❌ Before(意図せずNULLの行が母数から除外される)

SELECT AVG(rating) AS avg_rating FROM reviews;

✅ After(全注文を母数にしたい場合はCOUNT(*)と比較し、NULLの意味を明示する)

SELECT
  COUNT(*) AS total_orders,
  COUNT(rating) AS rated_orders,
  AVG(rating) AS avg_rating_of_rated_only
FROM reviews;

COUNT(*)は行数そのものを数えるのに対し、COUNT(カラム名)はNULLを除いた件数を数える、という違いを理解しておくと、こうした事故を防げます。
このケース以来、私は集計クエリを書くときに必ず「NULLがどう扱われるか」を最初に確認するようにしています。

ANDやORとNULLの組み合わせも「不明」になる

NULLを含む条件式は、trueでもfalseでもなく「UNKNOWN(不明)」という第3の状態になります。
WHERE句はUNKNOWNの行を結果に含めないため、NULLが絡む条件は想定より件数が減ることがあります。

-- ageがNULLの行は、この条件では取得できない
SELECT * FROM users WHERE age > 20 OR age IS NULL;
-- ageがNULLの行も含めたい場合はIS NULLを明示的に追加する

「NULLかもしれないカラム」を条件に使うときは、IS NULLを明示的に組み合わせる必要があるかどうか、都度立ち止まって確認しましょう。

応用・一歩先の使い方

NULLを含むソートの挙動を理解する

MySQLでは、ORDER BYでNULLは他のどの値よりも小さい扱いになります。
昇順(ASC)だとNULLが先頭に、降順(DESC)だとNULLが末尾に来ます。

SELECT name, rating FROM reviews ORDER BY rating ASC;
-- rating未評価(NULL)の行が最初に表示される

これを避けたい場合は、ORDER BY rating IS NULL, rating ASCのように、NULLかどうかを判定する式を先にソートキーへ加えるテクニックがあります。

外部キーにNULLを許可する設計判断

「未確定」「任意項目」を表現したいカラムは、NULLを許可する設計にすることが多いです。
一方で、集計や結合(JOIN)のロジックが複雑になりやすいため、「本当にNULLが必要か、デフォルト値で代替できないか」を設計段階で検討する価値があります。
このあたりはテーブル設計の記事で改めて扱います。

まとめ

この記事のポイント

  • NULLは「不明・未入力」を表し、0や空文字とは異なる
  • NULLの判定には=ではなくIS NULLIS NOT NULLを使う
  • 集計関数(COUNT・AVG等)はNULLの行を計算から除外するため、母数のズレに注意する
  • ANDやORの条件にNULLが絡むと「不明」扱いになり、想定より件数が減ることがある

次に読むべき記事

  • SQL IN・BETWEENで複数条件・範囲を指定する
  • SQL 集計関数(COUNT・SUM・AVG・MAX・MIN)の使い方
  • SQL LEFT JOIN・RIGHT JOINの違いと使いどころ

タグ: SQL, 中級者向け, 基本文法

タイトルとURLをコピーしました