- データベース
- 技術 💻
SQLのNULLの落とし穴8選|NOT IN・三値論理
SQLのNULLって、「空っぽの値」だと思われがちなんですよね。
でも正体は「不明」という第3の真理値を連れてくる存在です。真と偽の二択で考えていると、条件式が静かに裏切ってきます。今回は、「NOT INで全行消えた」のような実害の大きいものを8つ集めました。1つでも知らなかったら儲けものです。
① NULL = NULL は、真になりません
すべての出発点です。
SELECT * FROM users WHERE nickname = NULL; -- 1行も返らない
NULLは「不明」なので、不明と不明を比べた結果も「不明(UNKNOWN)」です。真でも偽でもない、第3の答え。
そしてWHERE句は、真の行だけを通します。不明は通りません。
だからNULLの検索は IS NULL という専用構文になる。SQLが真・偽・不明の三値論理で動いている——この1点から、今日の話が全部始まります。
② NOT IN のリストにNULLが混ざると、全行消えます
三値論理の、いちばん派手な事故です。
SELECT * FROM orders
WHERE user_id NOT IN (SELECT user_id FROM blacklist);
-- blacklistのuser_idに1つでもNULLがあると → 結果0行
NOT INは「リストのどれとも等しくない」の判定ですが、NULLとの比較が「不明」になるため、全体が真になれません。結果、1行も通らない。
エラーは出ず、黙って0行。サブクエリの列にNULLが入った日から突然発症します。対策はNOT EXISTSを使うか、サブクエリでNULLを除外することです。
③ 「<> 'A'」に、NULLの行は含まれません
直感に反する定番が、これです。
SELECT * FROM users WHERE plan <> 'premium';
「premium以外の全員」のつもりですよね。でもplanがNULLの行は、返ってきません。NULL <> 'premium' が「不明」だからです。
「AとA以外に分ければ全部のはず」という二分法の直感が、NULLの存在で三分割になっている。「無料プランの人数が合わない」の犯人は、だいたいここにいます。
④ 集計関数は、NULLを黙って除外します
もうひとつの静かな罠です。AVG・SUM・COUNT(列名)は、NULLの行を計算に入れません。
-- 点数: 80, 90, NULL の3人
SELECT AVG(score) FROM exams; -- → 85(2人の平均!)
SELECT COUNT(*) FROM exams; -- → 3
SELECT COUNT(score) FROM exams; -- → 2
「未受験を0点として平均したい」なら、明示的に変換が要ります。COUNT(*)とCOUNT(列)が別物なのも、同じ理屈です。
集計のズレ調査では、まず「分母は何人か」から疑うんです。
⑤ Oracleでは、空文字がNULLになります
DB移行で踏む、有名な地雷です。
Oracleは、長さ0の文字列('')をNULLとして扱います。「空文字を入れたつもりが、NULLが入っている」わけです。
PostgreSQLやMySQLでは、空文字とNULLは別物。つまり WHERE col = '' が、Oracleでは絶対にヒットしません(NULLとの比較になるので)。
「同じデータ、同じSQLなのに、DBを替えたら挙動が変わった」——空文字まわりは、移行チェックリストの常連なんです。
⑥ ORDER BYでのNULLの位置は、DBで違います
並び替えたとき、NULLは先頭に来るのか、末尾に来るのか。
| DB | 昇順(ASC)でのNULLの位置 |
|---|---|
| PostgreSQL・Oracle | 末尾 |
| MySQL・SQL Server | 先頭 |
「新しい順に並べたら、日付未設定の行が一番上に居座る」という画面バグは、この差の産物です。
対策は、位置を明示すること。PostgreSQLなどでは ORDER BY col DESC NULLS LAST と書けます。NULLの居場所も、仕様として決める対象なんです。
⑦ 「NULLも含めて等しいか」を、1発で比較できます
NULLを考慮した比較を、こう書いていませんか。
WHERE a = b OR (a IS NULL AND b IS NULL)
これ、専用の演算子があります。
WHERE a IS NOT DISTINCT FROM b -- 標準SQL / PostgreSQLなど
WHERE a <=> b -- MySQL
「NULL同士なら真、片方だけNULLなら偽」という、人間の直感どおりの比較です。差分検出や変更チェックのSQLが、劇的に読みやすくなります。
知っている人が少ないぶん、レビューで書くと一目置かれる演算子です。
⑧ NULLは、「不明」と「該当なし」の二役を背負わされています
最後は、設計の話です。
電話番号がNULL——それは「聞いていない(不明)」なのか、「持っていない(該当なし)」なのか。NULLという1つの値に、意味の違う2つの状態を背負わせているのが、多くの事故の根なんです。
だから設計の定石はこうなります。
- 意味を区別したいなら、NULLに頼らず状態を別の列や値で表現する
- 意味が1つに決まる列は、
NOT NULL制約とデフォルト値で、そもそもNULLを入れない
NULLは便利な値ではなく、「考えることを先送りした印」——そう見ると、テーブル設計の景色が変わります。
番外編:手元で効く小ネタ
NULLを値に変える・値をNULLに変える
COALESCE(col, 0) は「NULLなら0」。逆にNULLIF(a, b) は「aとbが等しければNULL」。ゼロ除算回避の NULLIF(分母, 0) は定番の護身術です。
UNIQUE制約とNULLの関係も、DBで違います
「NULLは重複扱いしない(複数OK)」のDBが多数派ですが、SQL Serverのように1つしか許さないDBもあります。一意制約の列にNULLを許すときは、確認が要ります。
三値論理は、SQLの発明ではありません
真・偽・不明の論理学は、1920年ごろにポーランドの論理学者ウカシェヴィチらが研究していました。SQLは「現実のデータには不明がある」と認めた、正直な言語なんですね。
RJCでも、NULLと向き合っています
RJCが携わる保険や金融のシステムは、「未入力」と「該当なし」の区別が審査や計算の分岐になる世界です。⑧の設計論は、そのまま設計書のレビュー項目なんですよね。
こういう仕組みの話を、Techチームで共有しながら開発しています。
よくいただくご質問
Q. まずどれから押さえるべきですか?
A. ②のNOT INかなと思います。事故が派手で、しかも明日書くSQLで即防げるんですよね。
Q. NULLは使わない方がいいのですか?
A. ゼロにはできませんが、減らせます。⑧のとおりNOT NULL+デフォルト値を基本にして、NULLを「意図して許した列」だけに絞るのが定石です。
Q. 調査のコツはありますか?
A. 「件数が合わない」ときは、③と④を疑ってください。WHERE条件と集計のどちらかで、NULLが静かに抜けていることが本当に多いんです。
Q. 未経験からでも、分かるようになりますか?
A. なります。「NULLは空っぽではなく『わからない』」——この一文だけ握っていれば、①〜④は全部その応用で読めます。
「わからない」を、わかって扱う
今日の8つに共通していたのは、これでした。
NULLの事故は、エラーではなく「静かに行が消える」「静かに数字がズレる」形で来ます。三値論理を知らないと、原因に辿り着けません。
「この条件、NULLの行はどっちに落ちる?」——レビューでこれを問える人が、集計の信頼を守るんですよね。
みなさんがNULLで踏んだ事故、よかったら教えてください。
WE ARE HIRING
RJCで一緒に開発しながら、
成長を楽しみませんか?
RJCは、Web・モバイル・AIを活用した開発プロジェクトで、テックリードやPM・PMOも活躍するシステム開発会社です。会社を知る、待遇を確かめる、話を聞いてみる。気になるところ見てみてください!
- 127日年間休日
- 12時間平均残業時間
- 毎日ガチャ遊びココロも大切にする福利厚生。アマギフなどの賞品ラインナップ!
ほかにも、チケットレストラン、書籍読み放題、2年ごとの慰労報奨(休暇 or 金一封)、11期連続の黒字決算。