有什么办法可以绕过 Oracle 10g 对静态 IN 子句中 1000 个项目的限制?我有一个逗号分隔的列表,其中包含许多我想在 IN 子句中使用的 ID,有时这个列表可能超过 1000 个项目,此时 Oracle 会引发错误。查询类似于此...
select * from table1 where ID in (1,2,3,4,...,1001,1002,...)
将值放在临时表中,然后执行 select where id in (select id from temptable)
select column_X, ... from my_table
where ('magic', column_X ) in (
('magic', 1),
('magic', 2),
('magic', 3),
('magic', 4),
...
('magic', 99999)
) ...
我几乎可以肯定您可以使用 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,...)
您首先从哪里获得 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;
/
这是需要提示的少数情况之一,否则 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
我也来到这里寻找解决方案。
根据您需要查询的高端项目数,并假设您的项目是唯一的,您可以将查询拆分为 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 个查询,这将是不合适的。如果是这样,那么您可能应该更认真地考虑使用上面提供的全局临时表解决方案作为最“正确”的解决方案。此外,如果您的项目不是唯一的,您还需要解决批次中的重复结果。
是的,甲骨文的情况非常奇怪。
如果您在 IN 子句中指定 2000 个 id,它将失败。这失败了:
select ...
where id in (1,2,....2000)
但如果您只是将 2000 个 ID 放在另一个表(例如临时表)中,它将在以下查询中工作:
select ...
where id in (select userId
from temptable_with_2000_ids )
您可以做什么,实际上可以将记录拆分为 1000 条记录并逐组执行。
下面是一些 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
除了 usingIN
子句,您可以尝试JOIN
与另一个正在获取 id 的表一起使用。这样我们就不用担心限制了。只是我的一个想法。
代替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);
干杯