【2026年7月最新】SQL IN句完全ガイド|基本・NOT IN・サブクエリ・EXISTSとの比較・Claude Code活用

【2026年7月最新】SQL IN句完全ガイド|基本・NOT IN・サブクエリ・EXISTSとの比較・Claude Code活用

SQLでデータを検索するとき「この3つの都市の顧客を取得したい」「この商品コードの一覧に含まれる注文を抽出したい」という場面で使うのがIN句です。ORを複数並べるより簡潔に書け、サブクエリと組み合わせることで強力なフィルタリングが実現できます。

この記事では、IN句の基本構文から、NOT IN・サブクエリとの組み合わせ・EXISTSとの違い・NULLが含まれる場合の落とし穴・パフォーマンスの考え方・Claude CodeによるSQL自動生成まで、実践コードとともに解説します。

代表菅澤 代表菅澤
IN句は「複数の値の中に含まれるかを確認する」演算子です。使い方は単純ですが、NULLとNOT INの組み合わせ、大量データでのパフォーマンス、EXISTSとの使い分けなど、知らないと本番で問題になる落とし穴があります。今日はそこまで踏み込んで解説します。
✔️IN句の基本構文(WHERE column IN (値1, 値2, 値3))
✔️IN句 vs OR演算子:どちらを使うべきかの判断基準
✔️NOT INの使い方とNULLが混在すると全行ゼロになる罠
✔️IN句とサブクエリ(SELECT文)の組み合わせパターン
✔️IN vs EXISTS:実行計画・パフォーマンスの違いと選び方
✔️IN句のリストが長くなる場合のパフォーマンス対策
✔️Claude CodeでSQL IN句クエリを自動生成する方法
📌 この記事の結論
【2026年7月最新】SQL IN句完全ガイド|基本・NOT IN・サブクエリ・EXISTSとの比較・Claude Code活用
SQLのIN句を完全解説。基本的なIN句の使い方、NOT IN、IN句とORの違い、サブクエリとの組み合わせ、EXISTSとの性能比較、NULLが含まれる場合の落とし穴、Claude CodeでのSQL自動生成まで実例付きで網羅。

01 SQL IN句の基本構文と使い方 WHERE col IN (値リスト)の基本形とデータ型別の使い方

IN句はWHERE句の中で使い、指定した値のリストのいずれかに一致する行を取得します。

-- IN句の基本構文
SELECT カラム名
FROM テーブル名
WHERE カラム名 IN (値1, 値2, 値3, ...);

-- 数値型のIN句
SELECT *
FROM orders
WHERE status_id IN (1, 2, 5);
-- status_idが1・2・5のいずれかの注文を取得

-- 文字列型のIN句(シングルクォートで囲む)
SELECT *
FROM customers
WHERE prefecture IN ('東京都', '大阪府', '神奈川県');
-- 3都府県の顧客を取得

-- 日付型のIN句
SELECT *
FROM sales
WHERE sale_date IN ('2026-07-01', '2026-07-15', '2026-07-19');

-- 複数カラムの条件と組み合わせ
SELECT order_id, customer_name, total_amount
FROM orders
WHERE status_id IN (1, 2)
  AND total_amount >= 10000;
-- ステータスが1か2で、金額1万以上の注文

📚 用語解説

IN句の評価方法:WHERE col IN (v1, v2, v3) は内部的にcol = v1 OR col = v2 OR col = v3と同等に評価される。ただしINのリストは可読性が高く、最適化エンジンが効率的な実行計画を選択しやすい。IN句のリストはコンマ区切りの値の集合であり、重複した値は1つとして扱われる(SELECT * FROM t WHERE x IN (1, 1, 2) は x IN (1, 2) と同じ)。

データ型記述例注意点
数値(INT)WHERE id IN (1, 2, 3)クォート不要
文字列(VARCHAR)WHERE name IN ('田中', '山田')シングルクォートで囲む
日付(DATE)WHERE dt IN ('2026-01-01', '2026-01-15')ISO形式YYYY-MM-DD推奨
BOOLEANWHERE active IN (TRUE, FALSE)DBによって記述が異なる

02 IN句 vs OR演算子の違いとどちらを使うべきか 可読性とパフォーマンスの観点からの使い分け

IN句とOR演算子は同じ結果を返しますが、可読性と保守性の観点からIN句が推奨されます。

-- OR演算子で書く場合(値が増えると読みにくい)
SELECT *
FROM orders
WHERE status_id = 1
   OR status_id = 2
   OR status_id = 5
   OR status_id = 8
   OR status_id = 10;

-- IN句で書く場合(すっきりと読みやすい)
SELECT *
FROM orders
WHERE status_id IN (1, 2, 5, 8, 10);

-- 結果は完全に同じ
-- IN句のほうが:
-- ① 書く量が少ない
-- ② 値の追加・削除が簡単
-- ③ コードレビューで意図が伝わりやすい
💡 IN句とORはパフォーマンスがほぼ同じ

モダンなRDBMS(MySQL, PostgreSQL, SQL Server等)では、IN句とORはクエリオプティマイザによって同等の実行計画に変換されます。かつては「IN句はORより遅い」という説もありましたが、現在のDBではほぼ同等です。値が3つ以上になる場合は可読性の観点からIN句を使うのが推奨です。

03 NOT IN句の使い方と注意点 NOT INはNULLが含まれると結果が全件ゼロになる重大な落とし穴

-- NOT IN句の基本的な使い方
-- 「リストに含まれない行を取得する」
SELECT *
FROM orders
WHERE status_id NOT IN (3, 4, 9);
-- ステータスが3・4・9以外の注文を取得

-- 実際によく使うパターン
-- 「削除済み・キャンセル以外のすべての注文」
SELECT order_id, customer_name, total_amount
FROM orders
WHERE status_id NOT IN (8, 9)  -- 8=削除済み, 9=キャンセル
ORDER BY created_at DESC;
⚠️ NOT INにNULLが含まれると全件ゼロになる!

これはSQLの最大の落とし穴の一つです。NOT IN (1, 2, NULL) は内部的にcol != 1 AND col != 2 AND col != NULLと評価されます。NULLとの比較(col != NULL)は常にNULL(UNKNOWN)を返すため、AND条件全体がUNKNOWNになり、どの行もWHERE条件をパスしません。結果: 0件が返る。サブクエリでNOT INを使う場合、サブクエリの結果にNULLが含まれると同様に0件になります。対策: NOT INの代わりにNOT EXISTSを使う、またはWHERE col IS NOT NULLをサブクエリに追加する。

-- NOT INのNULL問題の実例
-- 例: productsテーブルにcategory_idがNULLの行がある場合

-- NG: サブクエリがNULLを返すとproducts全件が0件になる
SELECT *
FROM orders
WHERE product_id NOT IN (
    SELECT product_id FROM discontinued_products
    -- discontinued_productsにNULLのproduct_idがあると全件ゼロ!
);

-- 対策1: サブクエリにIS NOT NULLを追加
SELECT *
FROM orders
WHERE product_id NOT IN (
    SELECT product_id FROM discontinued_products
    WHERE product_id IS NOT NULL  -- NULLを除外
);

-- 対策2: NOT EXISTSを使う(NULLに安全)
SELECT *
FROM orders o
WHERE NOT EXISTS (
    SELECT 1
    FROM discontinued_products dp
    WHERE dp.product_id = o.product_id
);

📚 用語解説

SQLにおける3値論理(TRUE/FALSE/UNKNOWN):SQLはTRUE/FALSEの2値論理ではなく、NULLを含む3値論理(TRUE/FALSE/UNKNOWN)を使う。NULLとの比較(col = NULL、col != NULL)はすべてUNKNOWNを返す(TRUEでもFALSEでもない)。WHERE句の条件がUNKNOWNの行は結果に含まれない(FALSEと同様に扱われる)。NULL値の存在確認はcol IS NULLまたはcol IS NOT NULLで行う(col = NULLは使えない)。

04 IN句とサブクエリの組み合わせ 動的なリストをSELECTで生成してINに渡す強力なパターン

IN句の値リストを固定値でなくサブクエリ(SELECT文)で生成することで、動的なフィルタリングが可能になります。これはSQLの中でも最も頻繁に使われる強力なパターンの一つです。

-- IN句 + サブクエリの基本形
SELECT カラム
FROM テーブルA
WHERE カラム IN (
    SELECT カラム FROM テーブルB
    WHERE 条件
);

-- 実例1: 注文のある顧客だけを取得
SELECT customer_id, name, email
FROM customers
WHERE customer_id IN (
    SELECT DISTINCT customer_id
    FROM orders
    WHERE created_at >= '2026-01-01'  -- 今年注文した顧客のID
);

-- 実例2: 特定カテゴリの商品を購入した注文を取得
SELECT *
FROM orders
WHERE product_id IN (
    SELECT product_id
    FROM products
    WHERE category_id = 5  -- カテゴリID=5の商品
);

-- 実例3: 集計を使ったサブクエリ
-- 「売上総額が10万円以上の顧客の詳細を取得」
SELECT *
FROM customers
WHERE customer_id IN (
    SELECT customer_id
    FROM orders
    GROUP BY customer_id
    HAVING SUM(total_amount) >= 100000
);
外側のクエリ
customersから
取得
IN句のサブクエリを実行
ordersから
customer_idリスト生成
一致する行をフィルタ
customersのcustomer_id
がリストにあるか
結果を返す
条件に合う顧客行

05 IN vs EXISTS:どちらを選ぶべきか 実行計画・NULL安全性・相関サブクエリの観点での使い分け

📚 用語解説

EXISTS句:WHERE EXISTS (サブクエリ) は「サブクエリが1行以上結果を返すならTRUE」を意味する。INと異なり、EXISTSは相関サブクエリ(外側の行を参照するサブクエリ)と組み合わせて使うことが多い。SELECT 1(定数)をサブクエリのSELECT句に使うのは、EXISTSは存在確認しかしないためどの値を返しても良いから。NULLが含まれても正しく動作するためNOT INの代わりにNOT EXISTSを使うことが推奨される。

-- IN句で書く場合
SELECT *
FROM customers c
WHERE c.customer_id IN (
    SELECT o.customer_id
    FROM orders o
    WHERE o.status_id = 1
);

-- EXISTS句で書く場合(相関サブクエリ)
SELECT *
FROM customers c
WHERE EXISTS (
    SELECT 1
    FROM orders o
    WHERE o.customer_id = c.customer_id  -- 外側の行を参照(相関)
      AND o.status_id = 1
);

-- どちらも同じ結果だが実行計画が異なる場合がある
-- 最新DBのオプティマイザはどちらも最適化するが、
-- NULLが含まれる場合の挙動が決定的に違う
比較軸IN (サブクエリ)EXISTS
NULLへの安全性NOT INでNULL問題ありNOT EXISTSはNULL安全
サブクエリの実行先にサブクエリを実行(非相関)外側の行ごとにサブクエリ実行(相関)
結果が大きい場合IN句のリストが大きくなる早期打ち切りが効く場合あり
結果が小さい場合効率的なことが多い相関サブクエリは行ごとに実行
可読性直感的で分かりやすい慣れが必要だが正確な意図を表現
推奨ケース小〜中規模のリストNOT EXISTS(NULLに安全)、相関条件が複雑な場合
💡 現代のDBでIN vs EXISTSのパフォーマンス差

MySQL 5.6+、PostgreSQL、SQL Serverなどの最新DBでは、クエリオプティマイザがIN句をEXISTSに変換したり、ハッシュ結合・ネステッドループ結合などを自動選択します。「INはEXISTSより遅い」は古い話で、現在はほぼ同等のケースが多いです。EXPLAIN(実行計画)で確認するのが確実です。ただしNOT INのNULL問題はパフォーマンスではなく正確性の問題のためNOT EXISTSを優先すべきです。

06 IN句にNULLが含まれる場合の落とし穴 NULLはINで一致しない・NOT INで全件ゼロになる動作の理解

-- IN句でのNULLの動作

-- テストデータ
CREATE TABLE test_items (id INT, name VARCHAR(50));
INSERT INTO test_items VALUES (1, 'りんご'), (2, NULL), (3, 'みかん');

-- NULLはINで「一致」にならない
-- name IN ('りんご', NULL, 'みかん') は
-- name = 'りんご' OR name = NULL OR name = 'みかん'
-- name = NULL はUNKNOWN(TRUEにならない)

SELECT * FROM test_items WHERE name IN ('りんご', NULL);
-- 結果: id=1(りんご)のみ
-- id=2(NULL)は「NULLはNULLと等しくない」ため含まれない

-- NULLの行を取得するにはIS NULLを使う
SELECT * FROM test_items WHERE name IS NULL;
-- 結果: id=2

-- NULLを含む行も一緒に取得したい場合
SELECT * FROM test_items
WHERE name IN ('りんご', 'みかん') OR name IS NULL;
-- 結果: id=1, id=2, id=3
⚠️ NOT INとNULLの組み合わせは必ず把握する

NOT IN句のリスト(または参照するサブクエリの結果)にNULLが1つでも含まれると、WHERE条件が全行でUNKNOWNとなり、結果が常に0件になります。「さっきまで動いていたNOT INクエリが突然0件になった」という場合、参照先テーブルにNULLが追加されたことが原因であることが多いです。本番環境でのNOT INは特に注意が必要です。

07 IN句のパフォーマンスと最適化 大量リスト・インデックス・サブクエリの実行計画を考慮する

-- 1. インデックスを活用するIN句
-- IN句の対象カラムにインデックスがあれば効率的に検索できる
-- CREATE INDEX idx_status ON orders(status_id);

EXPLAIN SELECT *
FROM orders
WHERE status_id IN (1, 2, 3);
-- type=range でインデックスを使った範囲スキャン

-- 2. IN句のリストが大量の場合はJOINを検討
-- NG(リストが1000件以上になる場合)
SELECT * FROM orders
WHERE product_id IN (1, 2, 3, 4, ... 1000件 ...);

-- OK: 一時テーブルやJOINを使う
-- (対象IDをテーブルに入れておく)
SELECT o.*
FROM orders o
INNER JOIN target_products tp ON o.product_id = tp.product_id;

-- 3. サブクエリのIN句は相関させない(非相関サブクエリにする)
-- NG: 外側のクエリの行ごとにサブクエリが実行される
-- (実際にはオプティマイザが最適化するが、意図的に相関させない)
SELECT *
FROM customers c
WHERE c.customer_id IN (
    SELECT customer_id FROM orders
    WHERE orders.customer_id = c.customer_id  -- 相関(避けられるなら)
);

-- OK: 非相関サブクエリ(先にリストを作ってフィルタ)
SELECT *
FROM customers c
WHERE c.customer_id IN (
    SELECT DISTINCT customer_id FROM orders  -- 相関なし
);

📚 用語解説

実行計画(EXPLAIN):SQLクエリがどのように実行されるかの計画を表示するコマンド。MySQL/PostgreSQL/SQL Serverで使用可能。EXPLAINの出力でtypeがALLはテーブルフルスキャン(遅い)、refやrangeはインデックスを使用(速い)を意味する。IN句やWHERE句の効率化のためEXPLAINで実行計画を確認し、遅い場合はインデックスを追加するのが基本的なアプローチ。

クエリを書く
IN句でフィルタ条件
EXPLAINで実行計画確認
typeがALLなら要改善
rangeならOK
インデックス追加
IN句の対象カラムに
インデックス作成
大量リストはJOINに変更
1000件超はテーブルJOINが
パフォーマンス良いことも

08 Claude CodeでSQL IN句クエリを自動生成する テーブル構造と取得条件を伝えるだけで正確なSQLが即生成

Claude Codeを使えば、テーブルの構造と「何を取得したいか」を日本語で説明するだけで、適切なIN句やNOT EXISTS・JOINを使ったSQLクエリが即座に生成されます。EXPLAINの結果を貼って「このクエリを最適化して」という依頼にも対応できます。

ユースケースClaude Codeへの指示例
基本的なIN句「ordersテーブルのstatus_idが1,2,5の注文を取得するSQLを書いて。status_id, customer_name, total_amountを取得。」
サブクエリIN「customersテーブルから、ordersテーブルに1件以上注文がある顧客の一覧を取得するSQL。IN句とサブクエリを使って。」
NOT INのNULL対策「discontinued_productsにないproduct_idの注文を取得したい。NULLが含まれる可能性があるためNOT EXISTSで書いて。」
EXPLAIN最適化「以下のSQLのEXPLAINの出力がtype=ALLです。IN句の対象カラムにインデックスを追加する方法と、JOINで書き直すバリエーションを教えて。[SQLとEXPLAINを貼り付け]」
AI鬼管理山崎 AI鬼管理山崎
SQLのIN句で特に役立つのは「NOT INのNULL問題を自動的に回避したNOT EXISTSで書いて」という指示です。「NOT INを使うな、NOT EXISTSで書いて」と伝えるだけでNULL安全なSQLが返ってくるため、NULLが混在するDBでの誤クエリ防止に効果的です。

📚 用語解説

相関サブクエリ:外側のSELECT文(主クエリ)の行を参照するサブクエリ。外側の行が変わるたびにサブクエリが再実行される。EXISTSと組み合わせる場合に多い。例: WHERE EXISTS (SELECT 1 FROM orders o WHERE o.customer_id = c.customer_id) のようにc.customer_idで外側の行を参照するのが相関サブクエリ。非相関サブクエリは外側の行を参照せず先に1回だけ実行されてリストを生成する。IN (SELECT ...)は通常非相関サブクエリとして扱われる。

09 まとめ:SQL IN句の重要ポイント 基本から落とし穴・最適化まで

✔️IN句はWHERE col IN (v1, v2, v3)で複数値のOR条件を簡潔に書く
✔️OR演算子よりIN句のほうが可読性が高く保守しやすい
✔️NOT INのリストにNULLが含まれると全件ゼロ——NOT EXISTSで代替する
✔️IN句にサブクエリを組み合わせると動的フィルタリングが可能
✔️NOT INのNULL問題に対してNOT EXISTSは安全
✔️IN句の対象カラムにインデックスがあれば高速。EXPLAINで確認する
✔️大量リスト(1000件超)はIN句よりJOINを検討
✔️Claude Codeにテーブル構造と取得条件を伝えれば正確なSQLが即生成

SQLをAIで効率化・学習したい方へ

IN句に限らず、JOINの最適化・ウィンドウ関数・複雑なサブクエリはClaude Codeとの組み合わせで大幅に効率化できます。
AI鬼管理では、データ活用・SQL業務のAI活用設計・実装支援を行っています。

代表菅澤 代表菅澤
「このSQLが遅い原因を調べたい」「JOIN・IN・EXISTSのどれを使うべきかわからない」という相談は多くあります。EXPLAINの出力とSQLを貼るだけで、原因と改善策をClaude Codeが返してくれます。まずは無料相談でお気軽にどうぞ。

NEXT STEP

この記事の内容を、あなたのビジネスで
実践してみませんか?

AI活用を自社で回せるようになりたい方へ

AI鬼管理

Claude Code・Cowork導入支援から業務設計・社内浸透まで実践ベースで伴走。「自社で回せる組織」を90日で作る経営者向けトレーニング。

よくある質問

Q. SQLのIN句とは何ですか?どんな時に使いますか?

A. IN句はWHERE条件の中で「このカラムの値が、指定したリストのいずれかと一致する行」を取得するための演算子です。例えばWHERE prefecture IN ('東京都', '大阪府', '神奈川県')と書くと、prefectureが東京都・大阪府・神奈川県のいずれかの行を取得します。ORを複数並べるより簡潔で読みやすく、値を追加・削除しやすいため、複数の値でフィルタリングする場面に適しています。

Q. SQLのIN句とOR演算子は何が違いますか?

A. 結果は同じですが可読性と保守性が異なります。WHERE status_id IN (1, 2, 5) はWHERE status_id = 1 OR status_id = 2 OR status_id = 5と完全に等価です。IN句のほうが値が増えても一行にまとまって読みやすく、値の追加・削除も括弧内を変更するだけで済みます。現代のRDBMSではパフォーマンスもほぼ同等です。3つ以上の値でフィルタリングする場合はIN句を使うのが一般的なベストプラクティスです。

Q. NOT INを使ったら結果が0件になりました。なぜですか?

A. NOT INのリスト(またはサブクエリの結果)にNULLが含まれているのが原因です。SQLは3値論理(TRUE/FALSE/UNKNOWN)を使い、NULLとの比較は常にUNKNOWN(真でも偽でもない)を返します。NOT IN (1, 2, NULL) は内部的にcol != 1 AND col != 2 AND col != NULLと評価されますが、col != NULLがUNKNOWNのため全体がUNKNOWNになり0件が返ります。対策はNOT EXISTSを使うか、サブクエリにWHERE col IS NOT NULLを追加してNULLを除外することです。

Q. IN句のサブクエリとEXISTSではどちらが速いですか?

A. MySQL 5.6以降、PostgreSQL、SQL Serverなどの現代のRDBMSではクエリオプティマイザが自動最適化するため、ほぼ同等のパフォーマンスになることが多いです。ただしNULLの安全性という観点では、NOT INはNULLに脆弱(全件ゼロになる)なためNOT EXISTSを推奨します。EXPLAINで実行計画を確認し、typeがALLのフルスキャンになっているならインデックスを追加するのが最も効果的なパフォーマンス改善です。

Q. IN句を使う際にインデックスは効きますか?

A. はい、IN句の対象カラムにインデックスがあれば効率的に検索できます。EXPLAINを実行してtype=rangeが表示されればインデックスを使った範囲スキャンが行われています(type=ALLはフルスキャンで遅い)。インデックスはCREATE INDEX idx_name ON テーブル名(カラム名)で作成します。ただしIN句のリストが非常に大きい(数百〜数千件以上)場合は、対象IDを一時テーブルに格納してJOINするほうが効率的なケースもあります。

Q. Claude CodeでSQL IN句を使ったクエリを作ってもらうにはどう伝えればいいですか?

A. テーブル名・カラム名(CREATE TABLE文があればそのまま貼り付けると確実)と「何を取得したいか」を日本語で伝えるのが効果的です。例: 「ordersテーブル(order_id, customer_id, status_id, total_amount)から、status_idが1か2のorder_id, total_amountを取得するSQL。total_amountの高い順にソート。」と伝えると正確なSQLが返ってきます。NOT INのNULL問題が心配な場合は「NOT INは使わずNOT EXISTSで書いて」と明示すると安全なSQLが生成されます。

AIAI鬼管理

AI鬼管理へのお問い合わせ

この記事を読んで気になった方へ。
AI鬼管理の専門スタッフが、御社に最適な
業務自動化プランを無料でご提案します。

会社名を入力してください
業種を選択してください
お名前を入力してください
正しいメールアドレスを入力してください

1つ以上選択してください
1つ以上選択してください
月額コストを選択してください

約1時間のオンライン面談(Google Meet)です

空き枠を取得中...
面談日時を選択してください

予約確定後、Google Calendarの招待メールをお届けします。
しつこい営業は一切ございません。

監修 最終更新日: 2026年7月19日
菅澤孝平
菅澤 孝平 株式会社GENAI 代表取締役
  • AI業務自動化サービス「AI鬼管理」を運営 — Claude Code を活用し、経営者の業務を「AIエージェントに任せる仕組み」へ転換するパーソナルトレーニングを 伴走構築 で提供。日報・採用・問い合わせ対応・経費精算・議事録・データ集計・営業リスト等の定型業務を、AIに代行させる体制を経営者と一緒に作り込む
  • Claude Code 実装ノウハウを 経営者・法人クライアント に直接指導。生成AIを「便利ツール」ではなく 「業務を任せる存在」 として運用する手法を体系化
  • 「やらせ切る管理」メソッドの開発者。シンゲキ株式会社(2021年設立・鬼管理専門塾運営)にて累計3,000名以上の学習者を志望校合格に導いた管理メソッドを、AI × 経営者支援 に転用
  • 著書『3カ月で志望大学に合格できる鬼管理』(幻冬舎)、『親の過干渉こそ、最強の大学受験対策である。』(講談社)
  • メディア出演: REAL VALUE / カンニング竹山のイチバン研究所 / ええじゃないかBiz 他
  • 明治大学政治経済学部卒
現在は AI鬼管理(Claude Code活用の伴走型パーソナルトレーニング)を主事業とし、経営者と二人三脚で「AIに業務を任せる仕組み」を実装。「実行を強制する環境」を AI で構築する手法を、自社の実運用知見をもとに発信している。