【Oracle】IN句の1000件上限を回避する5つの方法

【Oracle】IN句の上限を回避する方法 Oracle

OracleのSQLで大量のデータを IN 句に指定しようとして、エラーになった経験はありませんか?

Oracleの IN 句には 1,000件まで という上限があります。1,001件以上を指定すると ORA-01795 エラーが発生します。

この記事では、IN句の1,000件制限を回避する5つの方法を、パフォーマンスの違い・メリット・デメリットとともに解説します。

スポンサーリンク

IN句の1,000件制限とは?

Oracleでは、IN 句に指定できるリテラル値は 最大1,000件 です。

-- 1,000件まではOK
SELECT * FROM employees
WHERE emp_id IN (1, 2, 3, ..., 1000);

-- 1,001件以上はエラー
SELECT * FROM employees
WHERE emp_id IN (1, 2, 3, ..., 1001);
-- ORA-01795: リスト内の式の最大数は 1000 です

この制限は リテラル値(直接記述した値)の場合のみ 適用されます。サブクエリで指定する場合は制限がありません。

回避方法1:OR で複数の IN 句に分割する

最もシンプルな方法です。1,000件ごとに分割して OR で結合します。

SELECT * FROM employees
WHERE emp_id IN (1, 2, 3, ..., 1000)
   OR emp_id IN (1001, 1002, 1003, ..., 2000)
   OR emp_id IN (2001, 2002, 2003, ..., 3000);
メリット デメリット
SQL構文がシンプルで理解しやすい 値が多いとSQLが非常に長くなる
既存のSQLを少し修正するだけで対応可能 ORの数が増えるとパフォーマンスが低下する可能性

回避方法2:複合条件(タプル比較)を使う

値とダミー値のペアにすることで、IN句の制限を回避できます。

SELECT * FROM employees
WHERE (emp_id, 1) IN (
  (1, 1), (2, 1), (3, 1), ..., (2000, 1)
);

1つ目の値が実際の条件値、2つ目の 1 はダミー値です。タプル比較の上限は 65,535件 なので、通常の用途であれば十分です。

-- 65,536件以上はエラー
-- ORA-00913: 値の個数が多すぎます
メリット デメリット
1つのIN句で最大65,535件まで対応 ダミー値が必要で可読性がやや低い
OR分割より見通しが良い 65,535件を超える場合は別の方法が必要

回避方法3:一時テーブル(GTT)を使う

大量の値を一時テーブルに格納し、JOINまたはサブクエリで参照する方法です。数万件以上の場合に適しています。

-- 1. グローバル一時テーブルを作成
CREATE GLOBAL TEMPORARY TABLE tmp_ids (
  id NUMBER
) ON COMMIT DELETE ROWS;

-- 2. 値を一括INSERT
INSERT INTO tmp_ids (id) VALUES (1);
INSERT INTO tmp_ids (id) VALUES (2);
-- ... 必要な数だけ

-- 3. JOINで結合
SELECT e.*
FROM employees e
JOIN tmp_ids t ON e.emp_id = t.id;
メリット デメリット
件数の上限がない 一時テーブルの作成が必要
大量データでもパフォーマンスが安定 INSERT処理のオーバーヘッドがある
インデックスを追加してさらに高速化可能 DDL権限(CREATE TABLE)が必要

回避方法4:サブクエリに変換する

条件値が別のテーブルに存在する場合は、IN句をサブクエリに置き換えるのが最も効率的です。

-- サブクエリで参照(件数制限なし)
SELECT * FROM employees
WHERE emp_id IN (
  SELECT target_id FROM target_list WHERE condition = 'active'
);

-- EXISTS に変換するとさらに高速な場合も
SELECT * FROM employees e
WHERE EXISTS (
  SELECT 1 FROM target_list t
  WHERE e.emp_id = t.target_id
  AND t.condition = 'active'
);
メリット デメリット
件数の上限がない 条件値がテーブルに存在する必要がある
パフォーマンスが良い(特にEXISTS) リテラル値の一覧には使えない
SQLがシンプル

回避方法5:XMLTABLEを使う

動的にリテラル値のリストを生成する場合に、XMLTABLEを使ってテーブル化できます。

SELECT * FROM employees
WHERE emp_id IN (
  SELECT TO_NUMBER(COLUMN_VALUE)
  FROM XMLTABLE(
    '1,2,3,1001,1002,2001,2002,3001'
  )
);

カンマ区切りの文字列をテーブルに変換できるため、動的SQLとの相性が良い方法です。

メリット デメリット
一時テーブルの作成が不要 大量データではパフォーマンスが低下
動的SQLと組み合わせやすい XML解析のオーバーヘッドがある

各方法の比較まとめ

方法 上限 パフォーマンス 手軽さ おすすめ場面
OR分割 なし(実質的に) 〜数千件の場合
タプル比較 65,535件 〜数万件の場合
一時テーブル なし 大量データ・繰り返し実行
サブクエリ/EXISTS なし 条件値がテーブルにある場合
XMLTABLE なし 低〜中 動的SQL・少量データ

他のDBMSのIN句上限

参考として、主要なデータベースのIN句上限を比較します。

DBMS IN句の上限
Oracle 1,000件
SQL Server 制限なし(パラメータは2,100個まで)
PostgreSQL 制限なし
MySQL 制限なし(max_allowed_packetに依存)

まとめ

  • OracleのIN句は リテラル値1,000件 が上限
  • 最も手軽な回避策は OR分割 または タプル比較
  • パフォーマンス重視なら 一時テーブル または EXISTS が最適
  • 条件値がテーブルにあるなら サブクエリ/EXISTS を使うのがベストプラクティス