SQL中优化IN的几种做法并不只看表面做法,关键还要理解相关条件、限制和后续影响。
最近对一个存储过程进行优化,这里记录一下IN在SQLServer中的一些优化和情况。

所以 UNION ALL其实是效率最好的,也就是 =ANY()
Select * from TableA where name IN ('A','B','C');Select * from TableA where name='A' OR name='B' OR name='C';
SELECT *FROM TableA aWHERE EXISTS (SELECT 1FROM ( VALUES ('A'),('B'),('C')) AS tmp(name) -- SQL Server/PostgreSQL/MySQL 8.0+ 语法SELECT TableA.*FROM TableAJOIN ( VALUES ('A'),('B'),('C')) AS tmp(name)ON TableA.name = tmp.nameSelect * from TableA where name='A' union allSelect * from TableA where name='B' union allSelect * from TableA where name='C ' 或Select * from TableA where name = ANY('A','B','C');实现
SELECT * from TableA WHERE A.name IN ('A','B','C') AND A.types IN ('1','3','7','8') AND A.age IN ('33','35','39') 存储过程
declare @name varchar(100)='A,B,C',@type varchar(100)='1,3,7,8',@age varchar(100)='33,35,39'--列转行 if object_id('tempdb..#MyTemp') is not null Begin DROP TABLE #MyTemp End Select * Into #MyTempTable1 From ( SELECT B.id,B.typeid FROM ( SELECT [value] = CONVERT(XML, '<v>' + REPLACE(@name,',', '</v><v>') + '</v>') ) A OUTER APPLY( SELECT id = N.v.value('.', 'nvarchar(100)'),typeid=1 FROM A.[value].nodes('/v') N(v) ) B Union SELECT D.id,D.typeid FROM ( SELECT [value] = CONVERT(XML, '<v>' + REPLACE(@type,',', '</v><v>') + '</v>') ) C OUTER APPLY( SELECT id = N.v.value('.', 'nvarchar(100)'),typeid=2 FROM C.[value].nodes('/v') N(v) ) D Union SELECT D.id,D.typeid FROM ( SELECT [value] = CONVERT(XML, '<v>' + REPLACE(@age,',', '</v><v>') + '</v>') ) E OUTER APPLY( SELECT id = N.v.value('.', 'nvarchar(100)'),typeid=3 FROM C.[value].nodes('/v') N(v) )F ) as T --EXISTS 子查询 Select * from TableA A where A.name IN (select id from #MyTemp where typeid=1) AND A.types IN (select id from #MyTemp where typeid=2) AND A.age IN (select id from #MyTemp where typeid=3) 有一个很奇怪的情况,当@name='A’时,存储过程中
Select * from TableA A where A.name IN (select id from #MyTemp where typeid=1) AND A.types IN (select id from #MyTemp where typeid=2) AND A.age IN (select id from #MyTemp where typeid=3)
A.name... 比 A.name='A' 或 A.name in ('A') 查询时间多了一倍。 types、age 确没影响
改成Join后,得到解决:
Select * from TableA A JOIN #MyTempTable1 B ON B.id = A.DATAAREAID AND B.typeid=1WHERE A.types IN (select id from #MyTemp where typeid=2) AND A.age IN (select id from #MyTemp where typeid=3)
在in 不走索引的情况下,union all 效率最高
再改进:
Select * from TableA A JOIN #MyTempTable1 B ON B.id = A.DATAAREAID AND B.typeid=1WHERE A.types = ANY(select id from #MyTemp where typeid=2) AND A.age = ANY(select id from #MyTemp where typeid=3)
如果有索引, 几种方式效率差不多。没有索引都避免不了全表查询。
IN 列表包含大量值(如上千个)时,或者优化器以为IN会包含大量值,
可能生成低效的执行计划。
改成Join 效果可能有质的飞跃。
如: A.name IN (select id from #MyTemp where typeid=1)
计划会以为 IN 里的值会很多,给出很糟糕的执行计划。
效率无绝对,由「数据量、索引、执行计划」决定,常规业务场景下的高效层级为:UNION ALL ≈ JOIN(等值连接) > EXISTS > IN > OR
但考虑到 union all 不去重的特性, join+临时表的用法应该是最值得推荐的。