最近又又又遇到了开发同事问Oracle 中in超过1000个值遇到ORA-01795: maximum number of expressions in a list is 1000怎么处理,之前也陆陆续续查过一些方法,汇总整理一把。当然,其中的一些方法只是保证它不报错,性能可能堪忧,尽量少用。
select * from table1 where ID in (1,2,3,4,...,1001,1002,...);
例如有10000个值要in,就拆成10个20个语句循环执行,然后再处理结果。如果能拆得比较小,SQL能走上索引,其实还可以;如果拆出来都是大表走全表扫描,那就窒息了。
还遇到过有种极致的拆法:拆成一条条ID=xxx去执行,如果数据量大,通常性能不如小批量的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,...)
优缺点跟上面类似
有些神奇的程序从某个/些表里查出来一堆结果,存到列表里,再放到另一个表的in中。相对来说这是最好改的,通常直接改为表关联,或者将结果插入临时表后进行关联即可。
select where id in (select id from temptable);
还遇到过in中的数据是从缓存中取出、或者程序构造的,并不在DB的表里,就需要先构造。可以是直接insert,也可以利用变量+CTE,例如:
var b1 varchar2(2000);
exec :b1:='1,2,3,…,1002';
with str_list as
(select cast(REGEXP_SUBSTR( :b1,'[^,]+', 1, level) as number) as value
from dual
connect by level <= regexp_count(:b1, '[^,]+')
)
SELECT object_name FROM t1
where t1.object_id in (select value from str_list);
当然,如果in中的字符很长,很可能变量长度hold不住。
名字叫Multiple Row and Column Subqueries,使用这种语法,in中值的上限可以到10万而不是1000,足够满足绝大多数业务场景。当然,如果真的in 10万个值,性能绝对够呛,慎用…
select column_X, ... from my_table
where ('magic', column_X ) in (
('magic', 1),
('magic', 2),
('magic', 3),
('magic', 4),
...
('magic', 99999)
)
简单例子
with grades as (
select 'Jim' usr, 'B' grade from dual
union all
select 'Bill', 'C' from dual
union all
select 'Tim', 'A' from dual
union all
select 'Jim', 'B+' from dual
)
select *
from grades
where (usr,grade) in (('Jim','B'),
('Tim','C'),
('Tim','A'));
后面如果学到新方法,再继续补充了~
参考
How to put more than 1000 values into an Oracle IN clause - Stack Overflow
Is it possible to compare tuples in oracle-compatible sql? - Stack Overflow
《专题培训-SQL写法与改写》