SQL における NULL の扱いは多くの開発者を悩ませるポイントです。
特に、次のような比較がうまく動かない経験は誰でもあると思います。
SELECT * FROM users WHERE name = NULL; -- 取れない
SELECT * FROM users WHERE name <> NULL; -- 取れない
これは、SQL 標準仕様で
NULL を比較演算子(= や <>)で評価すると、結果が必ず NULL(UNKNOWN)になる
というルールがあるためです。
🔥 MySQL 独自の NULL 安全比較 <=> とは?
MySQL には、他の主要 RDBMS には存在しない特別な演算子<=>が存在します。
これは NULL セーフ等価比較(NULL-safe equal) と呼ばれます。
NULL を含む比較であっても TRUE / FALSE を返す、MySQL 独自の比較演算子と理解すれば OK です。
✔ 動作仕様
| 式 | 結果 |
|---|---|
| 1 <=> 1 | TRUE |
| 1 <=> 2 | FALSE |
| 1 <=> NULL | FALSE |
| NULL <=> NULL | TRUE |
通常の = と最も異なる点はここ:
NULL <=> NULL が TRUE になる。
SQL 標準では NULL = NULL は UNKNOWN(NULL)。
しかし <=> は「両方 NULL のときは等しい」と判断します。
🔎 例:通常の比較との違いを確認する
以下は MySQL のテーブル例と比較結果です。
CREATE TABLE samples (
id INT,
val INT
);
INSERT INTO samples VALUES
(1, 100),
(2, NULL),
(3, 100),
(4, NULL);
■ = を使う場合(NULL は比較できない)
SELECT * FROM samples WHERE val = NULL;
→ 1 行も返らない
理由:val = NULL は常に NULL(UNKNOWN)になり、WHERE では除外されるため。
■ <=> を使う場合(NULL も比較できる)
SELECT * FROM samples WHERE val <=> NULL;
→ val に NULL の行が返る
id | val
-----------
2 | NULL
4 | NULL
🎯 典型的なユースケース
1. NULL を含む「等価検索」を行いたい時
-- val が NULL の行も含めて比較したい
SELECT * FROM samples
WHERE val <=> :value;
アプリケーションから検索値に NULL が渡されても、
WHERE val <=> NULL
として正しく動作します。
2. 「値が変化したかどうか」を比較したい時
例えば、UPDATE による変更判定。
-- 値が変化した行だけ更新
UPDATE users
SET updated_at = NOW()
WHERE NOT (users.value <=> :new_value);
<=> を使うことで、
NULL から値ありへの変更
値ありから NULL への変更
も確実に検知できる。
3. 一意性チェック(NULL を含む場合)
通常の = では NULL 同士が比較できないため、
アプリ側で NULL を意識した分岐が必要になる場面があります。
SELECT 1 FROM items
WHERE unique_key <=> :key_value;
NULL を含む一意チェックを簡潔に書けます。
🚨 注意点:SQL 標準ではない(MySQL / MariaDB 限定)
重要なポイント:
<=> は MySQL および MariaDB の独自仕様
PostgreSQL / SQL Server / Oracle / SQLite には存在しない
他の RDBMS に移植する可能性のあるアプリケーションでは使用を検討する必要があります。
(ただし、NULL の扱いを厳密にしたい場面では非常に便利で、
MySQL でしか動かないデメリット以上に、メリットが大きいことも多い。)
✔ まとめ
SQL 標準の = では NULL を比較できないため、比較したい場合は安全等価比較 <=> を使用する
<=> は NULL 同士の比較でも TRUE を返す
検索、変更検知、一意性チェックなどで非常に便利
<=> は MySQL / MariaDB 限定の機能なので移植には注意