118

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

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

11 回答 11

106

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

于 2008-12-30T13:38:04.530 回答
82
select column_X, ... from my_table
where ('magic', column_X ) in (
        ('magic', 1),
        ('magic', 2),
        ('magic', 3),
        ('magic', 4),
             ...
        ('magic', 99999)
    ) ...
于 2013-06-10T07:49:05.790 回答
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 语句中使用(可能稍后或在另一台服务器上或其他任何地方)。在这种情况下,答案是永远不要从数据库中提取它。要么存储在临时表中,要么只编写一个查询。

我认为可能有更好的方法来修改这段代码,让这条 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; 
/ 

这是需要提示的少数情况之一,否则 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
于 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) 的两个查询不会那么糟糕。这也假设每个查询本身并不是特别昂贵。

如果您的预期项目的典型数量变得更大(例如,在 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 中单独选择每个项目来压缩的,然后通过将所有列联合在一起来解压缩。解压缩中的 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

除了 usingIN子句,您可以尝试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 回答