こんにちは、かつコーチです。
「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 NULLとIS 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を無視する
COUNT・SUM・AVGといった集計関数は、対象カラムに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 NULL・IS NOT NULLを使う - 集計関数(COUNT・AVG等)はNULLの行を計算から除外するため、母数のズレに注意する
- ANDやORの条件にNULLが絡むと「不明」扱いになり、想定より件数が減ることがある
次に読むべき記事
- SQL IN・BETWEENで複数条件・範囲を指定する
- SQL 集計関数(COUNT・SUM・AVG・MAX・MIN)の使い方
- SQL LEFT JOIN・RIGHT JOINの違いと使いどころ
タグ: SQL, 中級者向け, 基本文法