如何将超过 1000 个值放入 Oracle IN 子句 [重复]
Posted
技术标签:
【中文标题】如何将超过 1000 个值放入 Oracle IN 子句 [重复]【英文标题】:How to put more than 1000 values into an Oracle IN clause [duplicate] 【发布时间】:2010-09-28 20:28:28 【问题描述】:有什么办法可以绕过 Oracle 10g 对静态 IN 子句中 1000 个项目的限制?我有一个逗号分隔的列表,其中包含许多我想在 IN 子句中使用的 ID,有时这个列表可能超过 1000 个项目,此时 Oracle 会引发错误。查询类似这样...
select * from table1 where ID in (1,2,3,4,...,1001,1002,...)
【问题讨论】:
你有什么样的客户? .Net、Java ...? 你试过扩展吗?即 WHERE (ID=1 OR ID=2 ....) 曾几何时,我把这个想法告诉了一位同事。多么糟糕的一个:经常在数据库上运行噩梦般的请求(应用程序生成超过 1000 个 OR 运算符)。建议在这样的解决方案中限制列表长度... @Ben - 很好奇你为什么将我的问题标记为重复问题,而不是在我问了 2 年多之后的问题。 去展示这已经变成了多少荒地。 【参考方案1】:将值放在临时表中,然后执行 select where id in (select id from temptable)
【讨论】:
我个人会将这些值放入临时表并使用 JOIN 来查询这些值。不过,我不知道这是否实际上是更好的性能。 @ocdecio - 我对 Oracle 10g 的测试显示,与 JOIN 相比,IN 的解释计划不同(而且显然更糟)。我个人会使用 JOIN,并建议其他人测试不同的方法来查看性能差异,而不是猜测。 但是如果他有 2000 个值,他将如何在单个数据库命中中插入临时表?最好编写一些逻辑并拆分为 1000、1000 条记录并创建动态查询,因为 peter severin 说 select * from table1 where ID in (1,2,3,4,...,1000) 或 ID in (1001, 1002,...,2000).. 使用此技术在批量插入临时表时获得良好的性能:***.com/questions/7195665/…。在我的测试中,这种技术将 30 秒的长查询缩短到 1 秒。 我无法在生产环境中创建临时表。大拇指向下【参考方案2】:您可以尝试使用以下表格:
select * from table1 where ID in (1,2,3,4,...,1000)
union all
select * from table1 where ID in (1001,1002,...)
【讨论】:
当没有创建临时表的权限时,这是一个超级解决方法.. 不适合提前不知道有多少个值的情况。例如,如果有 3005 个值,将有 4 个选择子句联合在一起 我正在使用控制台 C#,这种方式实际上还不错。我只需要计算值的#,取千位数,然后 - 1,然后提前动态添加行数。 这绝对是愚蠢的,如果你有某种分组运算符,它会完全改变查询的含义。 @Teejay,您仍然可以将此方法与分组一起使用,只需将所有内容放入括号中,然后从该结果集中进行选择和分组。示例:select a, count(*) from (select * from... union all select * from... union all select * from...) group by a【参考方案3】:使用 ...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
【讨论】:
【参考方案4】:您首先从哪里获得 id 列表?由于它们是您数据库中的 ID,它们是否来自之前的某个查询?
当我过去看到这个时,是因为:-
-
缺少一个引用表,正确的方法是添加新表,在该表上放置一个属性并加入它
从数据库中提取 id 列表,然后在后续 SQL 语句中使用(可能稍后或在另一台服务器上或其他)。在这种情况下,答案是永远不要从数据库中提取它。要么存储在临时表中,要么只编写一个查询。
我认为可能有更好的方法来重新编写这段代码,而不是让这条 SQL 语句正常工作。如果您提供更多详细信息,您可能会得到一些想法。
【讨论】:
好问题!我经常使用我已经发布的数组技术,但是当用户在用户界面数据网格中手动选择了多行时,我会使用它。但是,用户手动选择 >1000 行的可能性不大。 在我的例子中,ID 来自一个单独的数据库。我从 DB-A 获得 10k 个 ID,并且需要从 DB-B 为每个 ID 获取数据。目前正在做一个大规模的 IN (1,2,..,1000) 语句。但表现不稳定。我在这里寻找性能更高的替代品:) @Basil 尝试将 id 插入到临时表中,然后加入以获得您需要的结果。【参考方案5】:我几乎可以肯定您可以使用 OR 在多个 IN 之间拆分值:
select * from table1 where ID in (1,2,3,4,...,1000) or
ID in (1001,1002,...,2000)
【讨论】:
IN 子句中值的最大数量仍然是您不应该受到限制的限制之一。 可以这样做,但这意味着 Oracle 每次都会看到不同的查询,这意味着需要进行大量的硬解析,这会减慢速度。 我喜欢这个解决方案,这里发布的其他一些解决方案太费劲了! 但是如果他有 2000 个值,他将如何在单个数据库命中中插入临时表?这样比较好写一些逻辑,拆分成1000、1000条记录,创建动态查询.. 有一个概念叫临时表。我们有同样的要求。我们在哪里是一个具有某些数据的中间件,以及一次使用 1000 多个 ID 的客户查询。因此,对于每个请求,我们创建一个临时表,批量插入 ID 列表(我们使用 JPA 执行此操作,不需要太多代码),然后将这些临时表 ID 与实际表数据连接。 【参考方案6】:我也来到这里寻找解决方案。
根据您需要查询的高端项目数量,并假设您的项目是唯一的,您可以将您的查询拆分为 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 个查询,则此 不适合。如果是这样,那么您可能应该更认真地考虑使用上面提供的全局临时表解决方案作为最“正确”的解决方案。此外,如果您的项目不是唯一的,您还需要解决批次中的重复结果。
【讨论】:
他,我的解决方案更正确:)不需要临时表。 正确地认为代码有点太样板了。我们使用来自 google-collections 的 Lists.partition() 使其几乎是单行的 我没有看到任何优势。如果您不想使用 Oracle 集合或临时表,请使用 Peter Severin 的解决方案。与此解决方案相比,Peter Severin 的解决方案导致更少的数据库调用和更少的解析。在客户端也更容易,因为您只有一批。 我希望我有一个 .Partition() 方法/函数 =)【参考方案7】:而不是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);
干杯
【讨论】:
【参考方案8】:您可以尝试将JOIN
与另一个正在获取ID 的表一起使用,而不是使用IN
子句。这样我们就不用担心限制了。只是我的一个想法。
【讨论】:
我可以看到这是您对问题的第一个答案,因此您没有代表将其作为评论发布,但将来您可能需要考虑将其发布为评论。答案应该是解决问题的方法,您很有信心会解决问题。 那解决不了问题。连接将选择所有记录,而不是选择 IN 子句选择的一两个选择项。【参考方案9】:是的,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 条记录,并逐组执行。
【讨论】:
【参考方案10】:select column_X, ... from my_table
where ('magic', column_X ) in (
('magic', 1),
('magic', 2),
('magic', 3),
('magic', 4),
...
('magic', 99999)
) ...
【讨论】:
这是怎么回事? 因为 Oracle 就是这么 _ (令人震惊?)你不会相信它,直到你尝试它并看到它的工作原理!这比创建一个临时表要好,特别是如果你擅长 vi/vim/subl。 我无语了。查询本身没有错误,但在自我施加 5 分钟的 recv_timeout 限制后,我确实得到了 ORA-12609。有没有一个词可以形容震惊但又一点也不惊讶? 哇!不敢相信这真的会奏效,但确实有效。 之所以有效,是因为这是一个多值比较 IN 列表。 Oracle 实现了这个多值比较 IN 列表,其限制为 【参考方案11】:这里有一些 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
【讨论】:
以上是关于如何将超过 1000 个值放入 Oracle IN 子句 [重复]的主要内容,如果未能解决你的问题,请参考以下文章