设为首页 收藏本站
查看: 1894|回复: 0

[经验分享] SQL Server中INNER JOIN与子查询IN的性能测试

[复制链接]

尚未签到

发表于 2017-7-13 14:15:50 | 显示全部楼层 |阅读模式
  这个月碰到几个人问我关于“SQL SERVER中INNER JOIN 与 IN两种写法的性能孰优孰劣?”这个问题。其实这个概括起来就是SQL Server中INNER JOIN与子查询孰优孰劣(IN是子查询的实现方式之一,本篇还是只对比INNER JOIN与子查询IN的性能,如果展开INNER JOIN与子查询性能对比,范围太大了,没法一一详述)。下面这篇文章,我们就INNER JOIN与子查询IN这两种写法孰优孰劣,在不同场景下进行一下测试对比一下,希望能解答你心中的疑惑。
    下面例子以AdventureWorks2014为测试场景,测试表为Sales.SalesOrderHeader与Sales.SalesOrderDetail。 如下所示:


DBCC FREEPROCCACHE;GODBCC DROPCLEANBUFFERS;GO SET STATISTICS IO ON;SET STATISTICS TIME ON; SELECT  h.* FROM Sales.SalesOrderHeader hWHERE SalesOrderID IN ( SELECT SalesOrderID FROM Sales.SalesOrderDetail)   DSC0000.png

   DSC0001.png   




DBCC FREEPROCCACHE;GODBCC DROPCLEANBUFFERS;GOSET STATISTICS IO ON;SET STATISTICS TIME ON; SELECT h.* FROM Sales.SalesOrderHeader hINNER JOIN Sales.SalesOrderDetail d ON h.SalesOrderID = d.SalesOrderID  如下所示,两种写法的SQL的实际执行计划是几乎一致。而且对比IO开销也是一致。cpu time 与elapsed time 有所差别,这个是因为两者返回的数据有所差别的缘故(SQL 1 返回 31465行数据, SQL 2返回 121317行数据),两者在逻辑上实际上是不一致的。因为重复数据的缘故。撇开这个不谈,光从性能上来考察两种,它们几乎是一模一样。没有优劣之分。
  
   DSC0002.png
   DSC0003.png

  如果有人对上面的重复数据不明白的话,下面做个简单的例子演示给大家看看。如下所示,截图中INNER JOIN就会有重复数据。  




CREATE TABLE P(    PID    INT ,    Pname  VARCHAR(24)) INSERT INTO dbo.PSELECT 1, 'P1' UNION ALLSELECT 2, 'P2' UNION ALLSELECT 3, 'P3'  CREATE TABLE dbo.C(    CID       INT ,    PID       INT ,    Cname  VARCHAR(24)) INSERT INTO dbo.cSELECT 1, 1, 'C1' UNION ALLSELECT 2, 1, 'C2' UNION ALLSELECT 3, 2, 'C3' UNION ALLSELECT 3, 3, 'C4'
  
DSC0004.png
  
  其实下面SQL在逻辑上才是相等的,它们的实际执行计划与IO是一样的。没有优劣之分。  




SELECT  h.* FROM Sales.SalesOrderHeader hWHERE SalesOrderID IN ( SELECT SalesOrderID FROM Sales.SalesOrderDetail);  SELECT DISTINCT h.* FROM Sales.SalesOrderHeader hINNER JOIN Sales.SalesOrderDetail d ON h.SalesOrderID = d.SalesOrderID;   DSC0005.png

  
  那么我们再来看另外一个例子,测试一下两者的性能差别。如下所示  




SET STATISTICS IO ON;SET STATISTICS TIME ON; SELECT  C.*FROM    Sales.Customer C        INNER JOIN Person.Person P ON C.PersonID = P.BusinessEntityID;  SELECT  C.*FROM    Sales.Customer CWHERE  C.PersonID IN ( SELECT Person.Person.BusinessEntityID                                     FROM   Person.Person );  

  INNER JOIN与子查询IN的实际执行计划对比的百分比为66% VS 34% , 子查询IN的性能还比 INNER JOIN的性能要好一些. IO几乎无差别,cpu time 与elapsed time的对比情况来看,子查询IN的性能确实要好一些。
  
  这个是因为子查询IN在这个上下文环境中,它使用右半连接(Right Semi Join)方式的Hash Match,即一个表中返回的行与另一个表中数据行进行不完全联接查询(查找到匹配的数据行就返回,不再继续查找)。那么可以肯定的是,在这个场景(上下文)中,子查询IN这种方式的SQL的性能比INNER JOIN 这种写法的SQL要好。
  
   DSC0006.png
   DSC0007.png
  
  
  那么我们再来看一个INNER JOIN性能比子查询(IN)要好的案例。如下所示,我们先构造测试数据。  




CREATE TABLE P(    P_ID    INT IDENTITY(1,1),    OTHERCOL        CHAR(500),    CONSTRAINT PK_P PRIMARY KEY(P_ID))GO BEGIN TRANDECLARE @I INT = 1WHILE @I<=10000BEGIN    INSERT INTO P VALUES (NEWID())    SET @I = @I+1    IF (@I%500)=0    BEGIN        IF @@TRANCOUNT>0        BEGIN            COMMIT            BEGIN TRAN        END    ENDENDIF @@TRANCOUNT>0BEGIN    COMMITENDGO  CREATE TABLE C (    C_ID  INT IDENTITY(1,1) ,    P_ID   INT  FOREIGN KEY REFERENCES P(P_ID),    COLN  CHAR(500),    CONSTRAINT PK_C  PRIMARY KEY (C_ID) )    SET NOCOUNT ON; DECLARE @I INT = 1WHILE @I<=1000000BEGIN    INSERT INTO C VALUES ( CAST(RAND()*10 AS INT)+1,  NEWID())    SET @I = @I+1ENDGO
  
  构造完测试数据后,我们对比下两者的性能差异  




SET STATISTICS IO ON;SET STATISTICS TIME ON; SELECT C.* FROM dbo.C CINNER JOIN dbo.P  P ON C.P_ID = P.P_IDWHERE P.P_ID=8  SELECT * FROM dbo.CWHERE P_ID IN (SELECT P_ID FROM dbo.P WHERE P_ID=8)
  
   DSC0008.png
   DSC0009.png
  
  增加对应的索引后,这个性能差距更更明显。 如下截图所示  




USE [AdventureWorks2014]GOCREATE NONCLUSTERED INDEX [IX_C_N1]ON [dbo].[C] ([P_ID])INCLUDE ([C_ID],[COLN])GO
   DSC00010.png
  
  在生产环境遇到一个案例, 两个视图使用INNER JOIN 与 IN 两种写法,在性能上差距很大。 使用子查询IN的性能比使用INNER JOIN的性能要好很多。如下截图所示。因为视图里面涉及多表。这样肯定导致执行计划非常复杂,导致SQL用INNER JOIN 的写法在性能上没有用子查询IN的写法要快
  
   DSC00011.png
  
  其实一部分情况下,INNER JOIN 与 子查询IN都是等价的。因为SQL Server优化器已经足够聪明,能够进行一些内部转换,生成等价的计划。但是在某一些特殊场景下,各有优劣。不能武断的就说INNER JOIN在性能上要比子查询IN要好。一定要结合上下文环境具体来谈性能优劣。否则没有多大意义。另外,子查询可以分为相关子查询和无关子查询,对于无关子查询来说,Not In子句比较常见,但Not In潜在会带来两种问题,结果不正确和性能问题,具体可以参考在SQL Server中为什么不建议使用Not In子查询
    

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-393439-1-1.html 上篇帖子: SQL Server 2016 下篇帖子: SQL Server里书签查找的性能伤害
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表