web-dev-qa-db-ja.com

ORA-01795の回避策はありますか:リスト内の式の最大数は1000エラーですか?

回避策はありますか

'ORA-01795: maximum number of expressions in a list is 1000 error'

クエリがあり、1つのフィールドの値に基づいてフィールドを選択しています。 in句を使用していますが、10000以上の値があります

例:

select field1, field2, field3 
from table1 
where name in 
(
'value1',
'value2',
...
'value10000+'
);

クエリを実行するたびに、ORA-01795: maximum number of expressions in a list is 1000 errorが取得されます。 TOADでクエリを実行しようとしていますが、違いはありません。同じエラーです。クエリを変更して機能させるにはどうすればよいですか?

前もって感謝します

62
valmont74

これを回避するには、複数の節を使用します。

select field1, field2, field3 from table1 
where  name in ('value1', 'value2', ..., 'value999') 
    or name in ('value1000', ..., 'value1999') 
    or ...;
104
Fabian Barney

私は最近この問題に遭遇し、追加のIN句をつなぎ合わせずにそれを行う生意気な方法を見つけました

タプルを利用できます

SELECT field1, field2, field3
FROM table1
WHERE (1, name) IN ((1, value1), (1, value2), (1, value3),.....(1, value5000));

Oracleでは、1000を超えるタプルを使用できますが、単純な値は使用できません。詳細はこちら、

https://community.Oracle.com/message/3515498#3515498
そして
https://community.Oracle.com/thread/958612

これはもちろん、IN内でサブクエリを使用して一時テーブルから必要な値を取得するオプションがない場合です。

22
MangoCrysis

いくつかの回避策は次のとおりです。

1- IN句をリテラルが1000未満の複数のIN句に分割し、OR句を使用してそれらを結合します。

元の「WHERE」句を1つの「IN」条件から複数の「IN」条件に分割します。

Select id from x where id in (1, 2, ..., 1000,…,1500);

に:

Select id from x where id in (1, 2, ..., 999) OR id in (1000,...,1500);

2-タプルの使用:1000の制限は、単一アイテムのセットに適用されます:(x)IN((1)、(2)、(3)、...)。セットに2つ以上のアイテムが含まれる場合、制限はありません:(x、0)IN((1,0)、(2,0)、(3,0)、...):

Select id from x where (x.id, 0) IN ((1, 0), (2, 0), (3, 0),.....(n, 0));

3-一時テーブルの使用:

Select id from x where id in (select id from <temporary-table>);
18
Ahmed MANSOUR

もう1つの方法:

CREATE OR REPLACE TYPE TYPE_TABLE_OF_VARCHAR2 AS TABLE OF VARCHAR(100);
-- ...
SELECT field1, field2, field3
  FROM table1
  WHERE name IN (
    SELECT * FROM table (SELECT CAST(? AS TYPE_TABLE_OF_VARCHAR2) FROM dual)
  );

私はそれが最適だとは思いませんが、うまくいきます。 Oracleは渡された配列のカーディナリティを理解せず、最適な実行プランを推定できないため、ヒント/*+ CARDINALITY(...) */は非常に便利です。

別の代替方法として、一時テーブルへのバッチ挿入と、IN述部の最後のサブクエリの使用。

7
svaor

in句の内部で内部クエリを使用してください:

select col1, col2, col3... from table1
 where id in (select id from table2 where conditions...)
7
Vikas Kumar

この問題を解決する別の方法もあります。 2つのテーブルTable1とTable2があるとします。また、Criteriaクエリを使用して、Table2で参照/存在しないTable1のすべてのエントリを取得する必要があります。このように先に進みます...

List list=new ArrayList(); 
Criteria cr=session.createCriteria(Table1.class);
cr.add(Restrictions.sqlRestriction("this_.id not in (select t2.t1_id from Table2 t2 )"));
.
.

。 。 。 Hibernateフレームワークによって変換されたSQLに1000以上のパラメーターを含めることなく、SQLですべてのサブクエリ機能を直接実行します。それは私のために働いた。注:要件に応じてSQL部分を変更する必要がある場合があります。

3
Fahim Ashraf

別のオプションがあります:with構文。 OPの例を使用すると、これは次のようになります。

with data as (
  select 'value1' name from dual
  union all
  select 'value2' name from dual
  union all
...
  select 'value10000+' name from dual)
select field1, field2, field3 
from table1 t1
inner join data on t1.name = data.name;

私はこの問題に遭遇しました。私の場合、Javaにデータのリストがあり、各アイテムにはitem_idとcustomer_idがありました。 DBには2つのテーブルがあり、それぞれの顧客に対するアイテムのサブスクリプションがあります。アイテムIDとともに、そのアイテムのアイテムまたは顧客へのすべてのサブスクリプションのリストを取得します。

私は3つのバリエーションを試しました:

  1. Javaから複数選択(タプルを使用して制限を回避)
  2. 構文あり
  3. 一時テーブル

オプション1:Javaからの複数選択

基本的に、私は最初に

select item_id, token 
from item_subs 
where (item_id, 0) in ((:item_id_0, 0)...(:item_id_n, 0))

それから

select cus_id, token 
from cus_subs 
where (cus_id, 0) in ((:cus_id_0, 0)...(:cus_id_n, 0))

次に、Javaにマップを作成し、cus_idをキーとして、アイテムのリストを値として、見つかった顧客サブスクリプションごとに(最初の選択から返されたリストに)関連するすべてのエントリを追加しますそのitem_idを持つアイテム。それははるかに厄介なコードです

オプション2:With-syntax

次のようなSQLですべてを一度に取得します

with data as (
  select :item_id_0 item_id, :cus_id_0 cus_id
  union all
  ...
  select :item_id_n item_id, :cus_id_n cus_id )
select I.item_id item_id, I.token token
from item_subs I
inner join data D on I.item_id = D.item_id
union all
select D.item_id item_id, C.token token
from cus_subs C
inner join data D on C.cus_id = D.cus_id

オプション3:一時テーブル

Rownr(主キー)、item_id、cus_idの3つのフィールドを持つグローバル一時テーブルを作成します。すべてのデータをそこに挿入し、オプション2と非常によく似た選択を実行しますが、with dataの代わりに一時テーブルでリンクします

パフォーマンス

これは、not完全に科学的なパフォーマンス分析です。

  • 私は開発用データベースに対して実行していますが、サブスクリプションを検索するデータセットに1000行をわずかに超えています。
  • 試したデータセットは1つだけです。
  • DBサーバーと同じ物理的な場所にいるわけではありません。それほど遠くはありませんが、自宅からVPN経由で試してみると、同じ距離であるにもかかわらず、はるかに遅くなります(そして、問題は自宅のインターネットではありません)。
  • 私は完全な呼び出しをテストしていたので、APIが別の(devの同じインスタンスで実行されている)を呼び出します。これもDBに接続して初期データセットを取得します。しかし、それは3つのケースすべてで同じです。

YMMV。

ただし、一時テーブルオプションはmuchより低速でした。ダブルのようにとても遅い。オプション1では14〜15秒、オプション2では15〜16秒、オプション3では30秒でした。

DBサーバーと同じネットワークからそれらを再試行し、機会があればそれが変化するかどうかを確認します。

3
Adam

演算子ユニオン

select * from tableA where tableA.Field1 in (1,2,...999)
union
select * from tableA where tableA.Field1 in (1000,1001,...1999)
union
select * from tableA where tableA.Field1 in (2000,2001,...2999)
2
Nikolai Nechai

これは古い質問であり、TOADを参照していますが、c#を使用してこれをコーディングする必要がある場合は、forループでリストを分割できます。 subList()を使用して、基本的にJavaでも同じことができます。

    List<Address> allAddresses = GetAllAddresses();
    List<Employee> employees = GetAllEmployees(); // count > 1000

    List<Address> addresses = new List<Address>();

    for (int i = 0; i < employees.Count; i += 1000)
    {
        int count = ((employees.Count - i) < 1000) ? (employees.Count - i) - 1 : 1000;
        var query = (from address in allAddresses
                     where employees.GetRange(i, count).Contains(address.EmployeeId)
                     && address.State == "UT"
                     select address).ToList();

        addresses.AddRange(query);
    }

これが誰かを助けることを願っています。

2
Tyler

他のソリューションは古いフレームワークを使用して実装するのが難しかったので、あなたの配列の分離を行う回避策もあります。

select * from tableA where id = 1 or id = 2 or id = 3 ...

しかし、パフォーマンスを向上させるには、可能であれば、Nikolai Nechaiのソリューションと組合を使用します。

0
El El