118

静的な IN 句で 1000 項目という Oracle 10g の制限を回避する方法はありますか? IN 句で使用したい多くの ID のカンマ区切りのリストがあります。このリストが 1000 項目を超える場合があり、その時点で Oracle がエラーをスローします。クエリはこれに似ています...

select * from table1 where ID in (1,2,3,4,...,1001,1002,...)
4

11 に答える 11

106

一時テーブルに値を入れてから、select where id in (temptable から id を選択) を実行します。

于 2008-12-30T13:38:04.530 に答える
71

OR を使用して複数の IN に値を分割できることはほぼ確実です。

select * from table1 where ID in (1,2,3,4,...,1000) or 
ID in (1001,1002,...,2000)
于 2008-12-31T11:16:48.543 に答える
53

次のフォームを使用してみてください。

select * from table1 where ID in (1,2,3,4,...,1000)
union all
select * from table1 where ID in (1001,1002,...)
于 2008-12-30T13:40:09.190 に答える
8

そもそもIDのリストはどこから取得しますか? これらはデータベース内の ID であるため、以前のクエリから取得したものですか?

私が過去にこれを見たとき、それは次の理由でした:-

  1. 参照テーブルがありません。新しいテーブルを追加し、そのテーブルに属性を設定して結合するのが正しい方法です。
  2. ID のリストがデータベースから抽出され、後続の SQL ステートメントで使用されます (おそらく後で、または別のサーバー上で)。この場合の答えは、データベースから決して抽出しないことです。一時テーブルに保存するか、クエリを 1 つだけ記述します。

このSQLステートメントを機能させるだけで、このコードを作り直すより良い方法があると思います。詳細を提供すると、いくつかのアイデアが得られるかもしれません。

于 2008-12-31T10:32:19.727 に答える
5

... 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 がクエリを何度もハード解析する必要がないことです。一時テーブルを使用すると、ほとんどの場合遅くなります。

edit 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
于 2008-12-30T13:55:28.630 に答える
4

私も解決策を探してここにたどり着きました。

クエリを実行する必要があるアイテムの上限数に応じて、アイテムが一意であると仮定すると、クエリを 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 つのクエリはそれほど悪くはありません。これは、各クエリ自体が特に高価ではないことも前提としています。

これは、予想されるアイテムの通常の数がはるかに大きくなった場合 (たとえば、100000 の範囲)、100 のクエリが必要な場合には適切ではありません。もしそうなら、おそらく最も「正しい」解決策として、上記で提供されたグローバル一時テーブルの解決策を使用することをもっと真剣に検討する必要があります。さらに、アイテムが一意でない場合は、バッチ内の重複した結果も解決する必要があります。

于 2009-08-03T19:37:00.420 に答える
2

はい、オラクルにとって非常に奇妙な状況です。

IN 句内に 2000 個の ID を指定すると、失敗します。これは失敗します:

select ... 
where id in (1,2,....2000) 

ただし、単に 2000 の ID を別のテーブル (一時テーブルなど) に配置すると、クエリの下で機能します。

select ... 
where id in (select userId 
             from temptable_with_2000_ids ) 

あなたができることは、実際にレコードを多くの 1000 レコードに分割し、グループごとに実行することです。

于 2013-05-08T19:39:00.930 に答える
1

インライン ビューを作成し、そこから選択することで制限を回避しようとする 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 id in ($bases_list_expr)
UPDATE
于 2014-05-30T17:40:37.767 に答える
0

IN句を使用する代わりにJOIN、IDを取得している他のテーブルで使用してみてください。そうすれば、制限について心配する必要はありません。私の側からの考えです。

于 2012-08-01T12:54:34.180 に答える
-2

それ以外のSELECT * FROM table1 WHERE ID IN (1,2,3,4,...,1000);

これを使って :

SELECT * FROM table1 WHERE ID IN (SELECT rownum AS ID FROM dual connect BY level <= 1000);

*これが依存関係である場合、ID が他の外部 IDS を参照していないことを確認する必要があることに注意してください。既存の ID のみが使用可能であることを確認するには、次のようにします。

SELECT * FROM table1 WHERE ID IN (SELECT distinct(ID) FROM tablewhereidsareavailable);

乾杯

于 2012-06-14T15:39:43.230 に答える