如何将超过1000个值放入Oracle IN子句中

是否有任何方法来解决静态IN子句中的1000个项目的Oracle 10g限制? 我有一个逗号分隔的列表,我想在IN子句中使用很多ID,有时这个列表可能会超过1000个项目,在这一点上Oracle会抛出一个错误。 查询与此类似…

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

把值放在一个临时表中,然后做一个select in id(从temptable中selectid)

您可以尝试使用以下forms:

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

我几乎可以肯定你可以使用OR分割多个IN中的值:

 select * from table1 where ID in (1,2,3,4,...,1000) or ID in (1001,1002,...,2000) 
 select column_X, ... from my_table where ('magic', column_X ) in ( ('magic', 1), ('magic', 2), ('magic', 3), ('magic', 4), ... ('magic', 99999) ) ... 

您从哪里获得ID列表? 由于它们是数据库中的ID,它们是否来自以前的一些查询?

当我看到过去的时候,是因为:

  1. 一个引用表丢失了,正确的方法是添加新的表,在该表上放置一个属性并join到该表中
  2. 从数据库中提取一个id列表,然后在后续的SQL语句中使用(也许在后面或在另一台服务器上或其他地方)。 在这种情况下,答案是永远不要从数据库中提取它。 可以存储在临时表中,也可以只写一个查询。

我认为可能有更好的方法来修改这个代码,只是让这个SQL语句正常工作。 如果你提供更多的细节,你可能会有一些想法。

使用…从表(…:

 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; / 

这是您需要提示的罕见情况之一,否则Oracle不会使用列标识的索引。 这种方法的优点之一是Oracle不需要一次又一次地parsing查询。 使用临时表大部分时间都比较慢。

编辑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 

我也在这里寻找解决scheme。

根据您需要查询的项目的高端数量,并假设您的项目是唯一的,您可以将您的查询拆分为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)查询不会那么差。 这也假设每个查询本身并不昂贵。

如果您的预期项目的典型数量要大得多(例如,在100000范围内),则需要100个查询,这是不合适的。 如果是这样,那么你应该更加认真地考虑使用上面提供的全球临时表解决scheme作为最“正确”的解决scheme。 此外,如果您的项目不是唯一的,则需要在批次中解决重复的结果。

是的,对于oracle来说很奇怪的情况。

如果在IN子句中指定了2000个ID,它将会失败。 这失败了:

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

但是如果你只是把2000个ID放在另一个表(例如临时表)中,它就可以工作:

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

你可以做什么,实际上可以将logging分成许多1000条logging,并逐组执行。

而不是使用IN子句,你可以尝试使用JOIN与另一个表,这是获取ID。 这样我们不必担心限制。 只是在我身边的一个想法。

这里有一些Perl代码试图通过创build一个内联视图,然后从中进行select来解决这个限制。 语句文本通过使用12个项目的行来压缩,而不是从DUAL中单独select每个项目,然后通过将所有列联合在一起来解压缩。 UNION或UNION ALL在解压缩时应该没有什么区别,因为它们都在一个IN内部,在join它之前会施加唯一性,但是在压缩中,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 

而不是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);

干杯