【Oracle】IN句の使い方|複数値指定・サブクエリ・NOT INとNULL・EXISTSとの使い分け

【Oracle】IN句の使い方|複数値指定・サブクエリ・NOT INとNULL・EXISTSとの使い分け Oracle

Oracleで複数の値のどれかに一致する行を取得したいときは、IN 句を使います。department_id = 10 OR department_id = 20 のようにORを並べるより、department_id IN (10, 20) と書いたほうが短く読みやすくなります。

ただし、IN 句は便利な反面、サブクエリ、NOT INNULL、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

固定値で絞る場合は、次の形が基本です。数値はそのまま、文字列や日付は適切にリテラルや変換関数を使います。

oracle-in-basic.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つは、意味としてはほぼ同じです。

in-and-or.sql
-- 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 句では、列の型と比較する値の型をそろえるのが基本です。

in-literals.sql
-- 数値
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 で判定します。

in-null.sql
-- 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列を返す必要があります。

in-subquery.sql
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と職種の組み合わせで絞り込む場合です。

multi-column-in.sql
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件になるような事故が起きます。

not-in-null-problem.sql
-- 危険: 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になる条件として説明されています。

exists-vs-in.sql
-- 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 が発生します。

ora-01795-example.sql
-- 1000件を超える値をINリストへ直接並べるとORA-01795の原因になる
SELECT *
FROM orders
WHERE order_id IN (1, 2, 3, /* ... */, 1000, 1001);

この上限の回避策は、既存の ORA-01795: リストに指定できる式の最大数は1000です で詳しく整理しています。大量IDを扱うなら、INリストを分割するより、一時テーブルや通常テーブルに入れてJOINする設計のほうが安定しやすいです。

join-instead-of-large-in.sql
-- 大量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

EXISTS Condition – Oracle SQL Language Reference

Nulls – Oracle SQL Language Reference