静的IN句で1000アイテムというOracle 10gの制限を回避する方法はありますか? IN句で使用するIDの多くのコンマ区切りリストがあります。このリストは1000アイテムを超えることがあり、その時点でOracleはエラーをスローします。クエリはこれに似ています...
select * from table1 where ID in (1,2,3,4,...,1001,1002,...)
値を一時テーブルに入れてから、select where id in(temptableからidを選択)を実行します
ORを使用して複数のINに値を分割できることはほぼ確実です。
select * from table1 where ID in (1,2,3,4,...,1000) or
ID in (1001,1002,...,2000)
次のフォームを使用してみてください。
select * from table1 where ID in (1,2,3,4,...,1000)
union all
select * from table1 where ID in (1001,1002,...)
select column_X, ... from my_table
where ('magic', column_X ) in (
('magic', 1),
('magic', 2),
('magic', 3),
('magic', 4),
...
('magic', 99999)
) ...
そもそもどこからidのリストを取得しますか?これらはデータベース内のIDであるため、以前のクエリから取得したものですか
私が過去にこれを見たとき、それはそうであった:-
このSQLステートメントを機能させるだけで、このコードを修正するより良い方法があると思います。詳細を入力すると、いくつかのアイデアが得られます。
... from table(...を使用します。
create or replace type numbertype
as object
(nr number(20,10) )
/
create or replace type number_table
as table of numbertype
/
create or replace procedure tableselect
( p_numbers in number_table
, p_ref_result out sys_refcursor)
is
begin
open p_ref_result for
select *
from employees , (select /*+ cardinality(tab 10) */ tab.nr from table(p_numbers) tab) tbnrs
where id = tbnrs.nr;
end;
/
これは、ヒントが必要なまれなケースの1つです。それ以外の場合、Oracleは列IDのインデックスを使用しません。このアプローチの利点の1つは、Oracleがクエリを何度もハード解析する必要がないことです。ほとんどの場合、一時テーブルの使用は遅くなります。
編集1手順を簡略化(jimmyorrに感謝)+例
create or replace procedure tableselect
( p_numbers in number_table
, p_ref_result out sys_refcursor)
is
begin
open p_ref_result for
select /*+ cardinality(tab 10) */ emp.*
from employees emp
, table(p_numbers) tab
where tab.nr = id;
end;
/
例:
set serveroutput on
create table employees ( id number(10),name varchar2(100));
insert into employees values (3,'Raymond');
insert into employees values (4,'Hans');
commit;
declare
l_number number_table := number_table();
l_sys_refcursor sys_refcursor;
l_employee employees%rowtype;
begin
l_number.extend;
l_number(1) := numbertype(3);
l_number.extend;
l_number(2) := numbertype(4);
tableselect(l_number, l_sys_refcursor);
loop
fetch l_sys_refcursor into l_employee;
exit when l_sys_refcursor%notfound;
dbms_output.put_line(l_employee.name);
end loop;
close l_sys_refcursor;
end;
/
これは出力します:
Raymond
Hans
私もここで解決策を探しました。
クエリする必要があるアイテムの上限数に応じて、アイテムが一意であると仮定すると、クエリを1000アイテムのバッチクエリに分割し、代わりに結果を結合することができます(ここで擬似コード):
//remove dupes
items = items.RemoveDuplicates();
//how to break the items into 1000 item batches
batches = new batch list;
batch = new batch;
for (int i = 0; i < items.Count; i++)
{
if (batch.Count == 1000)
{
batches.Add(batch);
batch.Clear()
}
batch.Add(items[i]);
if (i == items.Count - 1)
{
//add the final batch (it has < 1000 items).
batches.Add(batch);
}
}
// now go query the db for each batch
results = new results;
foreach(batch in batches)
{
results.Add(query(batch));
}
これは、通常1000を超えるアイテムを持たないシナリオでは、良いトレードオフになる可能性があります。1000を超えるアイテムを使用すると、「ハイエンド」エッジケースシナリオになるためです。たとえば、1500個のアイテムがある場合、(1000、500)の2つのクエリはそれほど悪くありません。これはまた、各クエリがそれ自体で特に高価ではないことを前提としています。
このwould n'tは、予想されるアイテムの典型的な数がはるかに大きくなった場合、たとえば100000の範囲で-100個のクエリが必要な場合に適切です。もしそうなら、おそらく最も「正しい」ソリューションとして上記で提供されたグローバルテンポラリテーブルソリューションを使用することをより真剣に検討する必要があります。さらに、アイテムが一意でない場合は、バッチ内の重複した結果も解決する必要があります。
はい、Oracleにとっては非常に奇妙な状況です。
iN句内で2000個のIDを指定すると、失敗します。これは失敗します:
select ...
where id in (1,2,....2000)
しかし、単に2000のIDを別のテーブル(たとえば一時テーブル)に入れると、これは機能します:
select ...
where id in (select userId
from temptable_with_2000_ids )
あなたができることは、実際にレコードを1000個のレコードに分割し、グループごとに実行することができます。
IN
句を使用する代わりに、IDをフェッチしている他のテーブルでJOIN
を使用してみてください。そうすれば、制限について心配する必要はありません。私の側からの単なる考え。
以下は、インラインビューを作成してから選択することにより、制限を回避しようとするPerlコードです。ステートメントテキストは、DUALから各アイテムを個別に選択する代わりに、それぞれ12アイテムの行を使用して圧縮され、すべての列を結合して圧縮解除されます。圧縮解除のUNIONまたはUNION ALLは、いずれにせよ結合する前に一意性を課すIN内にすべて入っているため、ここでは違いはありませんが、圧縮では、UNION ALLを使用して多くの不必要な比較を防ぎます。フィルタリングするデータはすべて整数なので、引用は問題になりません。
#
# generate the innards of an IN expression with more than a thousand items
#
use English '-no_match_vars';
sub big_IN_list{
@_ < 13 and return join ', ',@_;
my $padding_required = (12 - (@_ % 12)) % 12;
# get first dozen and make length of @_ an even multiple of 12
my ($a,$b,$c,$d,$e,$f,$g,$h,$i,$j,$k,$l) = splice @_,0,12, ( ('NULL') x $padding_required );
my @dozens;
local $LIST_SEPARATOR = ', '; # how to join elements within each dozen
while(@_){
Push @dozens, "SELECT @{[ splice @_,0,12 ]} FROM DUAL"
};
$LIST_SEPARATOR = "\n union all\n "; # how to join @dozens
return <<"EXP";
WITH t AS (
select $a A, $b B, $c C, $d D, $e E, $f F, $g G, $h H, $i I, $j J, $k K, $l L FROM DUAL
union all
@dozens
)
select A from t union select B from t union select C from t union
select D from t union select E from t union select F from t union
select G from t union select H from t union select I from t union
select J from t union select K from t union select L from t
EXP
}
次のように使用します:
my $bases_list_expr = big_IN_list(list_your_bases());
$dbh->do(<<"UPDATE");
update bases_table set belong_to = 'us'
where whose_base in ($bases_list_expr)
UPDATE