Oracleで複数の値のどれかに一致する行を取得したいときは、IN 句を使います。department_id = 10 OR department_id = 20 のようにORを並べるより、department_id IN (10, 20) と書いたほうが短く読みやすくなります。
ただし、IN 句は便利な反面、サブクエリ、NOT IN と NULL、1000件制限、大量ID指定時の性能などでつまずきやすい構文でもあります。この記事では、Oracleの IN 句を基本から実務パターンまで整理します。
少数の固定値なら
IN (...)、別テーブルの結果で絞るなら IN (SELECT ...)、大量件数や相関条件が絡むなら EXISTS やJOINを検討します。NOT IN はサブクエリ結果に NULL が混ざると期待通りに返らないことがあるため、NOT EXISTS を優先すると安全です。また、OracleのINリストには1000件制限があるため、大量値は既存のORA-01795対策を使います。IN句の使い分け早見表
最初に、どの書き方を選ぶかを整理しておきます。Oracleの IN 句は便利ですが、件数、NULL、別テーブルとの関係で適した書き方が変わります。
| やりたいこと | まず選ぶ書き方 | 注意点 |
|---|---|---|
| 少数の固定値で絞る | IN (値1, 値2, ...) |
同じ列への等価条件に向いている |
| 別テーブルの値で絞る | IN (SELECT ...) |
サブクエリは比較対象と同じ意味の1列を返す |
| 関連行があるか判定する | EXISTS |
相関条件を書き、実行計画も確認する |
| 関連行がないものを探す | NOT EXISTS |
NOT IN はNULL混入に弱い |
| 1000件を超えるIDで絞る | JOINまたは一時テーブル化 | 値を直接並べ続けるとORA-01795の原因になる |
まず使うSQL
固定値で絞る場合は、次の形が基本です。数値はそのまま、文字列や日付は適切にリテラルや変換関数を使います。
SELECT
employee_id,
employee_name,
department_id
FROM employees
WHERE department_id IN (10, 20, 30)
ORDER BY employee_id;
IN (10, 20, 30) は、対象列が10、20、30のいずれかに一致する行を返します。条件が増えても読みやすく、OR条件の括弧ミスも減らせます。
IN句とOR条件の違い
IN は、同じ列に対する複数の等価条件をまとめたいときに向いています。次の2つは、意味としてはほぼ同じです。
-- ORで書く SELECT * FROM employees WHERE department_id = 10 OR department_id = 20 OR department_id = 30; -- INで書く SELECT * FROM employees WHERE department_id IN (10, 20, 30);
| 書き方 | 向いている場面 | 注意点 |
|---|---|---|
IN |
同じ列に複数の候補値を指定する | 大量件数では1000件制限や性能に注意 |
OR |
列が違う条件を組み合わせる | ANDと混ぜるときは括弧が重要 |
EXISTS |
別表に該当行が存在するか判定する | 相関条件を明確に書く |
文字列・数値・日付でIN句を使う
IN 句では、列の型と比較する値の型をそろえるのが基本です。
-- 数値
SELECT *
FROM employees
WHERE department_id IN (10, 20, 30);
-- 文字列
SELECT *
FROM orders
WHERE status IN ('NEW', 'PAID', 'SHIPPED');
-- 日付: DATEリテラルを使うとNLS_DATE_FORMATに依存しにくい
SELECT *
FROM orders
WHERE order_date IN (DATE '2026-05-01', DATE '2026-05-02');
日付を文字列で '2026/05/01' のように渡すと、セッションの NLS_DATE_FORMAT に依存して失敗したり、意図と違う解釈になることがあります。日付フォーマットの扱いは Oracleの日付フォーマット記事 も参考になります。
IN句でNULLを探すときの注意
IN リストに NULL を混ぜても、NULLの行を取得できるわけではありません。NULLかどうかは等価比較ではなく IS NULL で判定します。
-- NG: statusがNULLの行を取りたい意図があっても、このNULLでは一致しない
SELECT *
FROM orders
WHERE status IN ('NEW', 'PAID', NULL);
-- OK: NULLを含めて取得したいならIS NULLを分けて書く
SELECT *
FROM orders
WHERE status IN ('NEW', 'PAID')
OR status IS NULL;
アプリ側で検索条件を組み立てる場合も、NULLを通常の値と同じリストに入れるのではなく、IS NULL 条件を別に追加する形にすると意図が明確です。
サブクエリでIN句を使う
候補値が別テーブルから決まる場合は、IN (SELECT ...) を使えます。サブクエリは、左辺の列と比較できる1列を返す必要があります。
SELECT
o.order_id,
o.customer_id,
o.total_amount
FROM orders o
WHERE o.customer_id IN (
SELECT c.customer_id
FROM customers c
WHERE c.rank = 'A'
);
Oracleの比較条件では、サブクエリを指定する場合、サブクエリが返す値の数とデータ型が比較対象の式に対応している必要があります。複雑なサブクエリの整理は Oracleのサブクエリ完全ガイド にもつながります。
複数列のIN句
Oracleでは、複数列の組み合わせに対して IN を使うこともできます。たとえば、部署IDと職種の組み合わせで絞り込む場合です。
SELECT *
FROM employees
WHERE (department_id, job_id) IN (
(10, 'SALES'),
(20, 'ENGINEER'),
(30, 'MANAGER')
);
複数列の IN では、左辺と右辺の列数・順序・データ型をそろえます。組み合わせ条件を誤ると、意図しない行まで対象になるため、単純な department_id IN (...) とは分けて考えます。
NOT INとNULLの落とし穴
NOT IN は、候補値に含まれない行を取得する条件です。ただし、サブクエリ結果に NULL が含まれると、結果が0件になるような事故が起きます。
-- 危険: blocked_customers.customer_id にNULLが含まれると期待通りに返らない
SELECT *
FROM orders o
WHERE o.customer_id NOT IN (
SELECT b.customer_id
FROM blocked_customers b
);
-- 最低限NULLを除外する
SELECT *
FROM orders o
WHERE o.customer_id NOT IN (
SELECT b.customer_id
FROM blocked_customers b
WHERE b.customer_id IS NOT NULL
);
OracleではNULLを含む比較結果はTRUE/FALSEだけでなくUNKNOWNになり得ます。NOT IN はこの影響を受けやすいため、除外条件では NOT EXISTS を使うほうが安全です。汎用的な除外条件は SQLで一致しないデータを抽出する方法 でも整理しています。
EXISTSとの使い分け
EXISTS は、サブクエリが1行でも返すかどうかを判定します。Oracle公式ドキュメントでも、EXISTS はサブクエリが少なくとも1行を返す場合にTRUEになる条件として説明されています。
-- IN: 顧客IDがサブクエリ結果に含まれる注文
SELECT *
FROM orders o
WHERE o.customer_id IN (
SELECT c.customer_id
FROM customers c
WHERE c.rank = 'A'
);
-- EXISTS: 顧客表に該当行が存在する注文
SELECT *
FROM orders o
WHERE EXISTS (
SELECT 1
FROM customers c
WHERE c.customer_id = o.customer_id
AND c.rank = 'A'
);
| 条件 | 使いやすい場面 | 注意点 |
|---|---|---|
IN |
候補値の集合に含まれるか見たい | サブクエリ結果が大きい場合は実行計画を確認 |
EXISTS |
関連テーブルに該当行があるか見たい | 相関条件を書き忘れない |
NOT EXISTS |
関連テーブルに該当行がないものを探す | NOT IN + NULLより安全 |
1000件制限と大量INの注意
Oracleの IN リストには、リストに指定できる式の数に制限があります。1000件を超える値を直接並べると ORA-01795 が発生します。
-- 1000件を超える値をINリストへ直接並べるとORA-01795の原因になる SELECT * FROM orders WHERE order_id IN (1, 2, 3, /* ... */, 1000, 1001);
この上限の回避策は、既存の ORA-01795: リストに指定できる式の最大数は1000です で詳しく整理しています。大量IDを扱うなら、INリストを分割するより、一時テーブルや通常テーブルに入れてJOINする設計のほうが安定しやすいです。
-- 大量IDはテーブル化してJOINする SELECT o.* FROM orders o JOIN target_order_ids t ON t.order_id = o.order_id;
MySQLやSQL ServerのIN句上限との違いは、MySQLのIN句上限、SQL ServerのIN句上限 も参考になります。
IN句が遅いときの確認点
IN 句が遅いときは、IN句そのものだけでなく、対象列のインデックス、型の不一致、サブクエリの件数、統計情報を確認します。
| 確認点 | よくある原因 | 対策 |
|---|---|---|
| 対象列にインデックスがない | 候補値が少なくても全表走査になる | 検索列に適切なインデックスを検討 |
| 型がずれている | 暗黙変換でインデックスを使いにくい | 列の型と値の型を合わせる |
| 値が多すぎる | INリストが長く解析・最適化が重い | JOINや一時テーブルに切り替える |
| サブクエリが重い | 候補値の生成自体が遅い | サブクエリ単体の実行計画を確認 |
| NOT INにNULLが混ざる | UNKNOWNになり結果が崩れる | NOT EXISTSへ切り替える |
よくある質問
OracleのIN句は何件まで指定できますか?
Oracleでは、INリストに指定できる式の数に1000件制限があります。1000件を超える場合はORA-01795の原因になるため、JOINや一時テーブル化を検討します。
IN句とEXISTSはどちらが速いですか?
一概には決まりません。データ量、インデックス、統計情報、サブクエリの内容で実行計画が変わります。候補集合に含まれるかを見るならIN、関連行の存在を見るならEXISTSを基本にし、実行計画で確認します。
NOT INで結果が0件になるのはなぜですか?
サブクエリ結果にNULLが含まれている可能性があります。NULLを除外するか、NOT EXISTS に書き換えると安全です。
日付をIN句で指定するときの注意点は?
文字列日付をそのまま比較するとNLS設定に依存します。DATE 'YYYY-MM-DD' や TO_DATE を使い、列の型と比較値の型をそろえます。
IN句に空リストは指定できますか?
IN () のような空リストはSQLとして使えません。アプリ側で候補値が0件になる可能性がある場合は、SQLを発行しない、常に偽になる条件を使う、または検索条件自体を組み立て直すなど、空配列用の分岐を用意します。
まとめ
Oracleの IN 句は、複数値のどれかに一致する行を取得する基本的な条件です。少数の固定値なら IN (...)、別テーブルの結果なら IN (SELECT ...) を使うと読みやすくなります。
ただし、NOT IN とNULL、1000件制限、大量INの性能には注意が必要です。除外条件は NOT EXISTS、大量IDはJOINや一時テーブル化も含めて検討しましょう。
参考
Comparison Conditions – Oracle SQL Language Reference

