MsSql

关注公众号 jb51net

关闭
首页 > 数据库 > MsSql > SQL 优化IN

SQL中优化IN的几种方法

作者:焚 城

本文对比了SQL Server中IN操作符的几种替代方法及性能表现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧

一、前言

最近对一个存储过程进行优化,这里记录一下IN在SQLServer中的一些优化和情况。

二、能代替IN的几种方法效率对比

​​所以 UNION ALL其实是效率最好的,也就是 =ANY()

三、实际写法

3.1 IN

Select * from TableA where name IN ('A','B','C');

3.2 OR

Select * from TableA where name='A' OR name='B' OR name='C';

3.3 EXISTS

SELECT *
FROM TableA a
WHERE EXISTS (
SELECT 1
FROM ( VALUES  ('A'),('B'),('C')) AS tmp(name) 

3.4 JOIN

-- SQL Server/PostgreSQL/MySQL 8.0+ 语法
SELECT TableA.*
FROM TableA
JOIN ( VALUES  ('A'),('B'),('C')) AS tmp(name)
ON TableA.name = tmp.name

3.4 UNION ALL

Select * from TableA  where name='A'   union all
Select * from TableA  where name='B'   union all
Select * 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) 

四、实际情况

情况1

有一个很奇怪的情况,当@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=1
		WHERE
   A.types IN (select id from #MyTemp where typeid=2) 
   AND A.age IN (select id from #MyTemp where typeid=3) 
  

情况2

在in 不走索引的情况下,union all 效率最高
再改进:

Select * from TableA A 
  JOIN #MyTempTable1 B 
		  ON B.id = A.DATAAREAID 
		 AND B.typeid=1
		WHERE
   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+临时表的用法应该是最值得推荐的。

到此这篇关于SQL中优化IN的几种方法的文章就介绍到这了,更多相关SQL 优化IN内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文