1. MySQLのNOT IN句とは?―除外条件でデータ抽出をもっと便利に
MySQLでデータベース操作を行う際、特定の値や条件を“除外”してデータを取得したいケースは意外と多いものです。例えば、「退会済みユーザー以外のリストを表示したい」「ブラックリストのID以外のデータを集計したい」など、ビジネスや開発現場で頻繁に登場します。そんなとき活躍するのがNOT IN
句です。
NOT IN
句は、指定した値やサブクエリの結果に該当しないデータだけを簡単に抽出できる強力なSQLの条件式です。リストによる単純な除外はもちろん、動的なサブクエリと組み合わせることで、さまざまなパターンの“除外抽出”が可能となります。
ただし、NOT IN
には使い方によって注意が必要な点や、思わぬ落とし穴も存在します。特にNULL値を含む場合の挙動や、大規模データベースでのパフォーマンス問題、NOT EXISTS
との違いなど、実務レベルで押さえておきたいポイントがいくつかあります。
この記事では、MySQLのNOT IN
句の基本から応用、そして注意点や他の除外手段との比較まで、具体例を交えながら丁寧に解説します。初めて学ぶ方はもちろん、普段からSQLに触れている方でも役立つ情報を網羅しています。ぜひ最後までご覧いただき、ご自身のSQLスキル向上や業務効率化にお役立てください。
2. NOT INの基本構文と利用例
NOT IN
句は、MySQLで「指定した複数の値に一致しないデータ」を抽出したい場合に使います。構文自体はシンプルですが、実際の現場ではさまざまな場面で活躍します。ここでは、まず基本構文と、実用的な例を紹介します。
【基本構文】
SELECT カラム名 FROM テーブル名 WHERE カラム名 NOT IN (値1, 値2, ...);
単純なリストによる除外
例えば、ユーザー名が「山田」と「佐藤」以外のユーザーを取得したい場合は、次のようなSQL文になります。
SELECT * FROM users WHERE name NOT IN ('山田', '佐藤');
このクエリを実行すると、「山田」さんと「佐藤」さん以外のすべてのユーザー情報が取得できます。除外リストは値をカンマで区切って並べるだけなので、簡単に記述できる点が特徴です。
サブクエリを使った動的な除外
NOT IN
は、カッコ内にリストだけでなくサブクエリを使うこともできます。例えば、「特定の条件に該当するユーザーID以外を取得したい」という場合に便利です。
SELECT * FROM users
WHERE id NOT IN (SELECT user_id FROM blacklist WHERE is_active = 1);
この例では、blacklist
テーブル内で「有効なブラックリスト(is_active=1)」となっているユーザーIDを除外して、users
テーブルからそれ以外のユーザーを取得しています。こうしたサブクエリとの組み合わせにより、ビジネスロジックに柔軟に対応できるのがNOT IN
句の魅力です。
複数条件での応用
もし複数のカラムで同時に除外条件を指定したい場合、NOT IN
はシンプルな1カラムでの利用が基本ですが、応用としてサブクエリや結合(JOIN)と組み合わせることで複雑な条件にも対応可能です。これについては後述の応用テクニックで詳しく解説します。
このように、NOT IN
句は「指定リストまたはサブクエリで取得した値以外をまとめて抽出したい」ときに非常に便利です。まずは自分の抽出したいデータをイメージし、シンプルな除外リストからサブクエリまで幅広く使いこなしてみましょう。
3. NULL混在時の注意点
NOT IN
句を使う際、意外と見落としがちなのが「NULL値が混じっている場合の挙動」です。これはSQL初心者だけでなく、経験者でもうっかりミスにつながる“落とし穴”となっています。
なぜなら、NOT IN
の判定ロジックは通常の比較とは異なり、「NULLが含まれていると結果が変わる」という特徴があるからです。
NULLを含む場合の挙動
例えば、次のようなテーブルがあるとします。
-- users テーブル
id | name
---+------
1 | 佐藤
2 | 山田
3 | 鈴木
4 | 田中
-- blacklist テーブル
user_id
--------
1
NULL
このとき、次のSQL文を実行するとどうなるでしょうか?
SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM blacklist);
一見すると「user_id=1」以外のすべてのユーザー(id=2,3,4)が取得されそうに見えます。しかし実際は、1件も返ってきません。
なぜ何も返らないのか
その理由は「SQLの3値論理(TRUE/FALSE/UNKNOWN)」にあります。NOT IN
のリスト内にNULLが含まれている場合、比較結果が「UNKNOWN」となり、MySQLはその行を結果に含めないという動作をします。
つまり、どの値も「これに該当しない」とは断言できなくなるため、SQL全体の判定が「偽」になるのです。
よくあるトラブル例
特にサブクエリを使う場合、ブラックリストや退会リストのデータにNULLが入っていると、思った通りにデータが抽出できないことが多発します。
「データが一件も出てこない」「なぜか除外できていない」などの不具合の原因になりやすいので要注意です。
対策と回避方法
NULL混在による問題を防ぐには、NOT IN
の対象リストからNULLを除外することが重要です。具体的には、サブクエリで「IS NOT NULL」を組み合わせます。
SELECT * FROM users
WHERE id NOT IN (
SELECT user_id FROM blacklist WHERE user_id IS NOT NULL
);
このようにしておくと、ブラックリストにNULL値があっても正しく「該当しないユーザー」を抽出できます。
ポイントまとめ
NOT IN
のリストにNULLがあると、結果が全件返らない場合がある- サブクエリ+NOT INでは「IS NOT NULL」でNULL除外を忘れずに
- 「データが取得できない」ときはまずNULL混入を疑う
4. NOT IN vs NOT EXISTS ~代替手段の比較
MySQLで「除外条件」を指定したいとき、NOT IN
のほかにNOT EXISTS
という選択肢もよく使われます。どちらも似たような目的で利用できますが、その仕組みや挙動、パフォーマンス、NULL値の扱いには違いがあります。この章では、NOT IN
とNOT EXISTS
の使い分けや、それぞれのメリット・デメリットについて解説します。
基本構文の比較
【NOT INを使った除外】
SELECT * FROM users
WHERE id NOT IN (SELECT user_id FROM blacklist WHERE user_id IS NOT NULL);
【NOT EXISTSを使った除外】
SELECT * FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM blacklist b WHERE b.user_id = u.id
);
どちらも「ブラックリストに登録されていないユーザー」を抽出するクエリです。
NULL値への強さ
NOT IN
- サブクエリ側やリストに
NULL
が混ざると、意図通りに動かない(結果が0件になる場合がある) - 対策として「IS NOT NULL」の条件が必要
NOT EXISTS
- サブクエリの結果に
NULL
が含まれていても、正しく動作する - 基本的にNULL値の影響を受けないため、安全性が高い
パフォーマンスの違い
データ量やテーブル構造によって最適な書き方は変わりますが、一般的に…
- 小規模データやリストが固定のときは
NOT IN
でも問題ない - サブクエリが大きい場合や複雑な条件が絡む場合は
NOT EXISTS
やLEFT JOIN
の方がパフォーマンスが良いことが多い
特にブラックリストの件数が増えてくると、NOT EXISTS
の方が効率的に動作するケースが増えます。MySQLのバージョンやインデックス設計にもよりますが、NOT EXISTS
は「行ごとにサブクエリの存在チェックを行う」ため、インデックスが有効な場合は非常に高速です。
使い分けの目安
- NULL値が混入する可能性がある場合
→NOT EXISTS
を推奨 - 固定リストや単純な値の除外のみ
→NOT IN
でも問題なし - パフォーマンスを最重視する場合
→ EXPLAINで実行計画を確認し、状況に応じて選択(JOINやNOT EXISTSも検討)
サンプルケース
NOT INでの不具合例
-- blacklist.user_idにNULLが含まれる場合
SELECT * FROM users
WHERE id NOT IN (SELECT user_id FROM blacklist);
-- → 結果が0件になることも
NOT EXISTSでの安全な除外例
SELECT * FROM users u
WHERE NOT EXISTS (
SELECT 1 FROM blacklist b WHERE b.user_id = u.id
);
-- → NULLの有無にかかわらず正しく抽出できる
まとめ
NOT IN
はシンプルだが、NULLの混入に弱いNOT EXISTS
はNULLに強く、実務でもよく使われる- どちらを使うかは「データ内容」と「求めるパフォーマンス」に応じて使い分ける
5. パフォーマンス上の考察
SQLで大量データを扱う際、クエリのパフォーマンスは非常に重要です。NOT IN
やNOT EXISTS
を使う場合、条件やデータ量によっては処理速度に大きな違いが出ることがあります。この章では、NOT IN
句を中心にパフォーマンスへの影響や注意点、最適化のコツについて解説します。
NOT IN句のパフォーマンスの特徴
NOT IN
は、指定したリストやサブクエリの中に一致するデータがなければ、そのレコードを抽出するという仕組みです。小規模なリストやテーブルであれば高速に動作しますが、次のようなケースでは処理が遅くなることがあります。
- サブクエリ側のデータ件数が多い場合
- 除外対象カラムにインデックスが張られていない場合
- サブクエリにNULL値が混在している場合
特に、サブクエリの中に何万件、何十万件ものデータが存在し、さらにインデックスが設定されていない場合、MySQLは全件比較を行うため、極端に遅くなることがあります。
インデックス活用の重要性
除外対象となるカラム(たとえばuser_id
など)にインデックスを設定することで、MySQLは効率的に比較・抽出を行うことができます。サブクエリや結合(JOIN)に利用するカラムには、積極的にインデックスを設定しましょう。
CREATE INDEX idx_blacklist_user_id ON blacklist(user_id);
このようにインデックスを追加しておくと、NOT IN
やNOT EXISTS
のパフォーマンスが大きく改善することがあります。

NOT INとNOT EXISTSのパフォーマンス比較
- 小規模な固定リスト:
NOT IN
が高速 - サブクエリが大規模:
NOT EXISTS
やLEFT JOIN
を使った方が効率的
特にMySQLはバージョンやテーブル設計によって実行計画(EXPLAIN結果)が変わるため、パフォーマンスの最適化には実際にテストすることが大切です。
EXPLAINによる実行計画の確認
どのクエリが速いかを知るには、MySQLのEXPLAIN
コマンドを使いましょう。
EXPLAIN SELECT * FROM users WHERE id NOT IN (SELECT user_id FROM blacklist WHERE user_id IS NOT NULL);
これにより、どのインデックスが使われているか、どのテーブルがフルスキャンされているかなど、パフォーマンスに直結する情報が確認できます。
大規模データでの最適化例
- 必要に応じて一時テーブルにデータを格納し、サブクエリの負担を減らす
- どうしても遅い場合はバッチ処理やキャッシュを活用する
LEFT JOIN ... IS NULL
で書き換えてみる(状況によって高速化することがある)
ポイントまとめ
- サブクエリの件数が多い場合やインデックスがない場合は
NOT IN
は遅くなりやすい - インデックス設計やクエリの見直しでパフォーマンスを大きく改善できる
NOT EXISTS
やLEFT JOIN
も選択肢として検討し、実際にEXPLAINで効果を確かめる
実務や本番環境では、データ規模や利用頻度を考慮して最適なクエリを選択しましょう。
6. よくある使い方と応用テクニック
NOT IN
句はシンプルな除外処理だけでなく、応用することでより柔軟なデータ抽出が可能です。ここでは、現場でよく使われるパターンや、知っておくと便利な応用テクニックを紹介します。
複数カラムでの除外(複合キーの除外)
NOT IN
は基本的に1つのカラムに対して使いますが、「2つ以上のカラムの組み合わせで除外したい」というニーズもあります。この場合は複合条件でのNOT EXISTS
やLEFT JOIN
が適しています。
【例:受注テーブル(orders)で、特定の顧客IDと商品IDの組み合わせを除外する】
SELECT * FROM orders o
WHERE NOT EXISTS (
SELECT 1 FROM blacklist b
WHERE b.customer_id = o.customer_id
AND b.product_id = o.product_id
);
こうすることで、ブラックリストに登録された「顧客ID×商品ID」の組み合わせをまとめて除外できます。
部分一致(ワイルドカード)除外:NOT LIKEとの併用
NOT IN
は値の完全一致でしか使えませんが、「特定の文字列パターンを除外したい」ときはNOT LIKE
が便利です。たとえば、メールアドレスが「test@」で始まるユーザーを除外したい場合などです。
SELECT * FROM users WHERE email NOT LIKE 'test@%';
複数のパターンを同時に除外したい場合は、AND
で条件をつなげます。
SELECT * FROM users
WHERE email NOT LIKE 'test@%'
AND email NOT LIKE 'sample@%';
リスト数が多いときの工夫
NOT IN
に直接多くの値(数百~数千件)を並べると、SQLの可読性やパフォーマンスが低下します。
この場合は、一時テーブルやサブクエリを活用し、可読性と保守性を高めることがポイントです。
-- 例:blacklistテーブルに除外リストを保存
SELECT * FROM users
WHERE id NOT IN (SELECT user_id FROM blacklist WHERE user_id IS NOT NULL);
集計関数と組み合わせた除外
サブクエリで集計した結果に基づき、特定条件のデータを除外したい場合にもNOT IN
は有効です。
【例:今月注文していない顧客を抽出】
SELECT * FROM customers
WHERE id NOT IN (
SELECT customer_id FROM orders
WHERE order_date >= '2025-06-01'
AND order_date < '2025-07-01'
);
サブクエリの代わりにJOINを使う例
場合によっては、LEFT JOIN
+IS NULL
で同じ結果を得られることがあります。
パフォーマンスや可読性を考慮して、最適な方法を選びましょう。
SELECT u.*
FROM users u
LEFT JOIN blacklist b ON u.id = b.user_id
WHERE b.user_id IS NULL;
この方法は特にサブクエリのパフォーマンスが不安なときや、インデックスが有効に働く場合におすすめです。
ポイントまとめ
- 複数カラムの除外にはNOT EXISTSやJOINが便利
- 部分一致の除外はNOT LIKEと組み合わせる
- サブクエリや一時テーブルでリスト管理をすると運用しやすい
- JOIN+IS NULLはパフォーマンス改善にもつながる
7. FAQ(よくある質問)
MySQLのNOT IN
句に関して、現場でよくある質問やつまずきポイントをまとめました。実際に検索されやすい疑問を中心に、シンプルかつ実務的な回答を用意しています。
Q1. NOT IN
とIN
の違いは何ですか?
A.IN
は「指定したリストのいずれかに一致する」データを取得するのに対し、NOT IN
は「指定したリストのどれにも一致しない」データだけを取得するために使います。使い方はほぼ同じですが、除外条件にしたい場合はNOT IN
を選びます。
Q2. NOT IN
でNULL値があるとどうなりますか?
A.
リストやサブクエリにNULLが混ざると、NOT IN
は全件返さないまたは予期しない結果になる場合があります。必ず「IS NOT NULL」などでNULLを除外してから使うのが安全です。
Q3. NOT IN
とNOT EXISTS
の使い分けは?
A.
- NULL値の可能性がある場合やサブクエリを使う場合は、
NOT EXISTS
の方が確実です。 - 固定リストや単純な値の除外には
NOT IN
でも問題ありません。 - 実行計画やデータ件数によってはパフォーマンスも変わるので、状況ごとに使い分けましょう。
Q4. NOT IN
を使うとクエリが遅くなることがあります。対策は?
A.
- 除外対象カラムにインデックスを設定する
- サブクエリ内の件数を減らす、または事前に一時テーブルにデータを整理する
- 場合によっては
NOT EXISTS
やLEFT JOIN ... IS NULL
への書き換えも検討しましょう - EXPLAINで実行計画を確認し、ボトルネックを特定するのが有効です
Q5. 複数カラムで除外したい場合はどうすればいいですか?
A.NOT IN
は1カラム専用なので、2つ以上のカラムで複合的に除外したい場合はNOT EXISTS
やLEFT JOIN
を使います。サブクエリ内で複数カラムの条件を組み合わせて除外処理を実現しましょう。
Q6. サブクエリの結果が多い場合、注意すべき点は?
A.
サブクエリの結果件数が多いと、NOT IN
はパフォーマンスが悪化しやすいです。インデックスの活用、一時テーブルへの分割、サブクエリをできるだけ小さくまとめるなど、実装前に工夫するのがおすすめです。
Q7. どうしても意図した結果が出ない場合、どこを見直せばいいですか?
A.
- NULL値が混ざっていないかを確認
- サブクエリの結果が期待通りかを個別に実行して確かめる
- WHERE条件やJOINの結び方に誤りがないかチェック
- データベースのバージョンや設定で挙動が異なる場合もあるので、公式ドキュメントも参考にしましょう
8. まとめ
MySQLのNOT IN
句は、特定の条件に該当しないデータを効率よく抽出するために非常に便利な構文です。単純なリストによる除外から、サブクエリとの組み合わせによる柔軟なデータ抽出まで、さまざまな場面で活用できます。
一方で、NOT IN
句には「NULL値が含まれる場合の注意」や「大規模データにおけるパフォーマンス低下」など、実務で意識しておくべきポイントも存在します。特に、NULL混入による思わぬ全件除外や、サブクエリの件数が多い場合の速度低下など、SQL初心者だけでなく経験者も注意が必要です。
また、NOT EXISTS
やLEFT JOIN ... IS NULL
など、NOT IN
の代替手段も理解しておくことで、より安全かつ効率的なSQLを書くことができるようになります。
目的やデータ規模に応じて、最適な方法を選択してください。
ポイントのおさらい
NOT IN
は簡単な除外条件に有効- NULL値の影響には必ず注意(IS NOT NULLの併用を習慣に)
- パフォーマンスが気になる場合はインデックス設計やNOT EXISTS・JOINも選択肢に
- 必ず実行計画(EXPLAIN)で効果をチェック
SQLの「落とし穴」を避けながら、スマートなデータ抽出ができるよう、ぜひ本記事の内容を日々の業務や学習に役立ててください。