SQLでサブクエリを使って除外条件を指定する際に利用される「NOT IN」と「NOT EXISTS」。両者の動作の違いやNULLの扱い、パフォーマンス差を実例付きで徹底解説します。
EXISTS
は ANSI SQL(国際標準SQL)に含まれる構文 のため、
ほぼすべてのリレーショナルデータベースで利用できます。
古いバージョンの一部DBを除き、標準構文として移植性が非常に高いのが特徴です。
1. NOT IN と NOT EXISTS の基本構文
句 | 構文例 | 説明 |
---|---|---|
NOT IN | SELECT * FROM A WHERE ID NOT IN (SELECT ID FROM B); | サブクエリの結果に含まれないIDを抽出 |
NOT EXISTS | SELECT * FROM A WHERE NOT EXISTS (SELECT 1 FROM B WHERE A.ID = B.ID); | Bに同じIDが存在しない場合のみAを取得 |
ポイント:
-
両者とも「除外」目的だが、評価タイミングとNULL処理が異なる。
2. 動作の違い(NULLの扱いに注目)
条件 | NOT INの結果 | NOT EXISTSの結果 |
---|---|---|
サブクエリにNULLが含まれる | すべての行が除外される | 正常に比較できる |
サブクエリが空(0件) | 全件取得される | 全件取得される |
理由:NOT IN
は内部的に「A.ID <> B.ID」を繰り返すような処理を行うため、NULLが含まれると比較結果がUNKNOWNとなり、全体が評価されなくなる。
一方、NOT EXISTS
は行ごとに存在チェックを行うため、NULLの影響を受けない。
3. 実行結果の比較例
以下の例を見てみましょう。
テーブルA
ID | NAME |
---|---|
1 | 田中 |
2 | 鈴木 |
3 | 佐藤 |
テーブルB
ID |
---|
1 |
NULL |
4. パフォーマンスの違い
比較項目 | NOT IN | NOT EXISTS |
---|---|---|
NULLの影響 | 受ける | 受けない |
実行計画(最適化) | インデックス利用されにくい場合あり | 最適化されやすい |
大量データ時の効率 | 遅くなるケースあり | より安定して高速 |
Oracleの最適化傾向 | 半結合(Anti-Join)に変換されることあり | 同様に最適化される |
実測例(概略)
件数 | NOT IN所要時間 | NOT EXISTS所要時間 |
---|---|---|
1万件 | 0.25秒 | 0.20秒 |
10万件 | 3.1秒 | 1.8秒 |
※ 実測環境:Oracle 19c、インデックスあり、CPU 4コア相当
5. どちらを使うべきか
条件 | 推奨句 |
---|---|
サブクエリにNULLが含まれる可能性あり | NOT EXISTS |
データが小規模でNULLなし | どちらでも可 |
大規模データ・実行計画を重視 | NOT EXISTS(推奨) |
可読性を優先 | NOT EXISTS のほうが誤動作が少ない |
6. まとめ
観点 | 内容 |
---|---|
ANSI SQL対応 | ○(どのDBでも使用可能) |
実行パフォーマンス | DBごとに最適化される(MySQL 8以降で特に改善) |
推奨度 | 高い(NOT INより安全で移植性が高い) |
注意点 | MySQL 5.x 以前では最適化が弱いケースがある |
✔ 結論:
除外条件を指定する場合は、基本的に「NOT EXISTS」を使う方が安全で高速です。
ただし、NULLが確実に存在しないことが保証される小規模データではNOT IN
も選択肢になります。