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

时间:2008-12-30 13:35:18

标签: oracle in-clause

有没有办法绕过静态IN子句中1000项的Oracle 10g限制?我有一个以逗号分隔的列表,其中列出了我想在IN子句中使用的许多ID,有时这个列表可能超过1000个项目,此时Oracle会抛出错误。查询与此类似......

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

11 个答案:

答案 0 :(得分:88)

将值放在临时表中,然后执行select in where in(从temptable中选择id)

答案 1 :(得分:50)

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

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

答案 2 :(得分:45)

您可以尝试使用以下表单:

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

答案 3 :(得分:35)

select column_X, ... from my_table
where ('magic', column_X ) in (
        ('magic', 1),
        ('magic', 2),
        ('magic', 3),
        ('magic', 4),
             ...
        ('magic', 99999)
    ) ...

答案 4 :(得分:8)

首先从哪里获取ID列表?由于它们是数据库中的ID,它们是否来自之前的一些查询?

当我在过去看到这个时,原因是: -

  1. 缺少参考表,正确的方法是添加新表,在该表上放置一个属性并加入它
  2. 从数据库中提取id列表,然后在后续SQL语句中使用(可能在以后或在其他服务器或其他任何服务器上)。在这种情况下,答案是永远不要从数据库中提取它。要么存储在临时表中,要么只写一个查询。
  3. 我认为可能有更好的方法来重新编写这个代码,只是让这个SQL语句起作用。如果您提供更多详细信息,您可能会得到一些想法。

答案 5 :(得分:5)

使用...来自表格(...:

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将不会在列ID上使用索引。这种方法的一个优点是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

答案 6 :(得分: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)的查询不会那么糟糕。这也假定每个查询本身并不特别昂贵。

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

答案 7 :(得分:1)

是的,对于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条记录并逐组执行。

答案 8 :(得分:0)

您可以尝试将IN与另一个表(即获取ID)一起使用,而不是使用JOIN子句。这样我们就不用担心限制了。只是我身边的想法。

答案 9 :(得分:0)

这是一些Perl代码,它通过创建内联视图然后从中进行选择来尝试解决限制。语句文本通过使用每个十二个项目的行来压缩,而不是单独从DUAL中选择每个项目,然后通过将所有列联合在一起来解压缩。 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

答案 10 :(得分:-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);

干杯