回避策はありますか
'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でクエリを実行しようとしていますが、違いはありません。同じエラーです。クエリを変更して機能させるにはどうすればよいですか?
前もって感謝します
これを回避するには、複数の節を使用します。
select field1, field2, field3 from table1
where name in ('value1', 'value2', ..., 'value999')
or name in ('value1000', ..., 'value1999')
or ...;
私は最近この問題に遭遇し、追加の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内でサブクエリを使用して一時テーブルから必要な値を取得するオプションがない場合です。
いくつかの回避策は次のとおりです。
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>);
もう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
述部の最後のサブクエリの使用。
in
句の内部で内部クエリを使用してください:
select col1, col2, col3... from table1
where id in (select id from table2 where conditions...)
この問題を解決する別の方法もあります。 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部分を変更する必要がある場合があります。
別のオプションがあります: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からの複数選択
基本的に、私は最初に
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完全に科学的なパフォーマンス分析です。
YMMV。
ただし、一時テーブルオプションはmuchより低速でした。ダブルのようにとても遅い。オプション1では14〜15秒、オプション2では15〜16秒、オプション3では30秒でした。
DBサーバーと同じネットワークからそれらを再試行し、機会があればそれが変化するかどうかを確認します。
演算子ユニオン
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)
これは古い質問であり、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);
}
これが誰かを助けることを願っています。
他のソリューションは古いフレームワークを使用して実装するのが難しかったので、あなたの配列の分離を行う回避策もあります。
select * from tableA where id = 1 or id = 2 or id = 3 ...
しかし、パフォーマンスを向上させるには、可能であれば、Nikolai Nechaiのソリューションと組合を使用します。