前提データ
問題のSQLを実行するためのINSERT文を以下に示します。
CREATE TABLE users (
id INT
);
INSERT INTO users (id) VALUES
(1),
(2),
(3),
(NULL),
(5);
簡易的に確認したい場合は以下のサイトを利用することをお勧めします。
問題のSQL
-- 連番になっていないIDを探したい
SELECT MIN(id + 1) AS notseq
FROM users
WHERE ( id + 1 ) NOT IN (SELECT id FROM users);
原因:NOT IN と NULL
NULLは比較することができない
SQLにおいてNULLは、
- 空文字ではない
- 0ではない
- 不正値ではない
という「分からない」という状態を示しています。
つまり、値ではありません。
NOT INは比較する関数である
NOT INはある値xと別の値yを比較する仕組みになっています。
そのため、値ではないNULLが含まれてしまうと実行エラーにはならないものの、
条件がすべて UNKNOWN となり、結果がnullになります。
イメージとしては、
-
x = NULLは動かない -
x IS NULLは動く
という仕組みと同じですね。
=は比較する時に使用する演算子なので、比較できないNULLを=で比較することはできないのです。
三値論理について知ると理解が深まる
SQLの世界では、三値論理を採用しています。
| 値 | 意味 |
|---|---|
| TRUE | 条件成立 |
| FALSE | 条件不成立 |
| UNKNOWN | 不明 |
この3つめに存在しているUNKNOWNを生み出すのがNULLです。
比較時のイメージ
NULLは何と比較してもUNKNOWNになってしまいます。
10 = 10 → TRUE
10 = 20 → FALSE
10 = NULL → UNKNOWN
10 <> NULL → UNKNOWN
回避方法
回避方法はNOT EXISTSを利用することです。
NOT INは比較してしまうことでエラーの原因になりました。
ですが、NOT EXISTSは値同士で比較しません。
NOT EXISTSは「この条件を満たすレコードが1行でも存在するか?」を判別します。
そのため、値同士による比較を行いません。その結果、NULLがあるカラムを抽出する際にNULLを許容することができます。
結論:NOT EXISTS を使う
問題のSQLをNOT EXISTSを利用することで問題を回避してみましょう。
SELECT MIN(u1.id + 1) AS notseq
FROM users u1
WHERE NOT EXISTS (
SELECT 1
FROM users u2
WHERE u2.id = u1.id + 1
);
参考文系
- 達人に学ぶSQL徹底指南書 第2版 初級者で終わりたくないあなたへ(著者:ミック)

