这个月碰到几个人问我关于“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;

GO

DBCC DROPCLEANBUFFERS;

GO

 

SET STATISTICS IO ON;

SET STATISTICS TIME ON;

 

SELECT  h.* FROM 

Sales.SalesOrderHeader h

WHERE SalesOrderID IN ( SELECT SalesOrderID FROM Sales.SalesOrderDetail)

DBCC FREEPROCCACHE;

GO

DBCC DROPCLEANBUFFERS;

GO

SET STATISTICS IO ON;

SET STATISTICS TIME ON;

 

SELECT h.* FROM Sales.SalesOrderHeader h

INNER JOIN Sales.SalesOrderDetail d ON h.SalesOrderID = d.SalesOrderID

如下所示,两种写法的SQL的实际执行计划是几乎一致。而且对比IO开销也是一致。cpu time 与elapsed time 有所差别,这个是因为两者返回的数据有所差别的缘故(SQL 1 返回 31465行数据, SQL 2返回 121317行数据),两者在逻辑上实际上是不一致的。因为重复数据的缘故。撇开这个不谈,光从性能上来考察两种,它们几乎是一模一样。没有优劣之分。

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

CREATE TABLE P

(

    PID    INT ,

    Pname  VARCHAR(24)

)

 

INSERT INTO dbo.P

SELECT 1, 'P1' UNION ALL

SELECT 2, 'P2' UNION ALL

SELECT 3, 'P3'

 

 

CREATE TABLE dbo.C

(

    CID       INT ,

    PID       INT ,

    Cname  VARCHAR(24)

)

 

INSERT INTO dbo.c

SELECT 1, 1, 'C1' UNION ALL

SELECT 2, 1, 'C2' UNION ALL

SELECT 3, 2, 'C3' UNION ALL

SELECT 3, 3, 'C4'

其实下面SQL在逻辑上才是相等的,它们的实际执行计划与IO是一样的。没有优劣之分。

SELECT  h.* FROM 

Sales.SalesOrderHeader h

WHERE SalesOrderID IN ( SELECT SalesOrderID FROM Sales.SalesOrderDetail);

 

 

SELECT DISTINCT h.* FROM Sales.SalesOrderHeader h

INNER JOIN Sales.SalesOrderDetail d ON h.SalesOrderID = d.SalesOrderID;

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

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 C

WHERE  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要好。

那么我们再来看一个INNER JOIN性能比子查询(IN)要好的案例。如下所示,我们先构造测试数据。

CREATE TABLE P

(

    P_ID    INT IDENTITY(1,1),

    OTHERCOL        CHAR(500),

    CONSTRAINT PK_P PRIMARY KEY(P_ID)

)

GO

 

BEGIN TRAN

DECLARE @I INT = 1

WHILE @I<=10000

BEGIN

    INSERT INTO P VALUES (NEWID())

    SET @I = @I+1

    IF (@I%500)=0

    BEGIN

        IF @@TRANCOUNT>0

        BEGIN

            COMMIT

            BEGIN TRAN

        END

    END

END

IF @@TRANCOUNT>0

BEGIN

    COMMIT

END

GO

 

 

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 = 1

WHILE @I<=1000000

BEGIN

    INSERT INTO C VALUES ( CAST(RAND()*10 AS INT)+1,  NEWID())

    SET @I = @I+1

END

GO

构造完测试数据后,我们对比下两者的性能差异

SET STATISTICS IO ON;

SET STATISTICS TIME ON;

 

SELECT C.* FROM dbo.C C

INNER JOIN dbo.P  P ON C.P_ID = P.P_ID

WHERE P.P_ID=8

 

 

SELECT * FROM dbo.C

WHERE P_ID IN (SELECT P_ID FROM dbo.P WHERE P_ID=8)

增加对应的索引后,这个性能差距更更明显。 如下截图所示

 

USE [AdventureWorks2014]

GO

CREATE NONCLUSTERED INDEX [IX_C_N1]

ON [dbo].[C] ([P_ID])

INCLUDE ([C_ID],[COLN])

GO

在生产环境遇到一个案例, 两个视图使用INNER JOIN 与 IN 两种写法,在性能上差距很大。 使用子查询IN的性能比使用INNER JOIN的性能要好很多。如下截图所示。因为视图里面涉及多表。这样肯定导致执行计划非常复杂,导致SQL用INNER JOIN 的写法在性能上没有用子查询IN的写法要快

其实一部分情况下,INNER JOIN 与 子查询IN都是等价的。因为SQL Server优化器已经足够聪明,能够进行一些内部转换,生成等价的计划。但是在某一些特殊场景下,各有优劣。不能武断的就说INNER JOIN在性能上要比子查询IN要好。一定要结合上下文环境具体来谈性能优劣。否则没有多大意义。另外,子查询可以分为相关子查询和无关子查询,对于无关子查询来说,Not In子句比较常见,但Not In潜在会带来两种问题,结果不正确和性能问题,具体可以参考在SQL Server中为什么不建议使用Not In子查询

SQL Server中INNER JOIN与子查询IN的性能测试的更多相关文章

  1. SQL Server中Table字典数据的查询SQL示例代码

    SQL Server中Table字典数据的查询SQL示例代码 前言 在数据库系统原理与设计(第3版)教科书中这样写道: 数据库包含4类数据: 1.用户数据 2.元数据 3.索引 4.应用元数据 其中, ...

  2. SQL Server进阶(五)子查询

    概述 子查询的概念: 当一个查询是另一个查询的条件时,称之为子查询.子查询可以嵌套在主查询中所有位置,包括SELECT.FROM.WHERE.GROUP BY.HAVING.ORDER BY. 外面的 ...

  3. SQL SERVER技术内幕之4 子查询

    最外层查询的结果集会返回给调用者,称为外部查询.内部查询的结果是供外部查询使用的,也称为子查询.子查询可以分成独立子查询和相关子查询两类.独立子查询不依赖于它所属的外部查询,而相关子查询则须依赖它所属 ...

  4. SQL Server中一些不常见的查询

    把一些不常见但又会用到的SQL查询整理备份一下 --筛选出某个字段中包含中文的记录 SELECT * FROM temp WHERE W1 LIKE '%[吖-座]%' --筛选出某个字段在哪些表中存 ...

  5. SQL SERVER中生僻字问题存储与查询问题

    以下仅记录碰到的几个问题 1.首先字段设置为varchar的时候存储后无法进行正常的显示 显示为? 此状态下匹配查询或者Like模糊查询都没问题 2.将字段设置为nvarchar,在进行插入或者跟新时 ...

  6. sql server中数据约束相关的查询

    根据表名查找数据约束 SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'CMS_EventLog'; SEL ...

  7. SQL SERVER中XML查询:FOR XML指定PATH

    SQL SERVER中XML查询:FOR XML指定PATH 前言 在SQL SERVER中,XML查询能够指定RAW,AUTO,EXPLICIT,PATH.本文用一些实例介绍SQL SERVER中指 ...

  8. 在SQL Server中为什么不建议使用Not In子查询

        在SQL Server中,子查询可以分为相关子查询和无关子查询,对于无关子查询来说,Not In子句比较常见,但Not In潜在会带来下面两种问题: 结果不准确 查询性能低下       下面 ...

  9. (网页)在SQL Server中为什么不建议使用Not In子查询(转)

    转自博客园宋沄剑  英文名:CareySon : 在SQL Server中,子查询可以分为相关子查询和无关子查询,对于无关子查询来说,Not In子句比较常见,但Not In潜在会带来下面两种问题: ...

随机推荐

  1. C++ 网络爬虫实现

    最近有个概念吵得很火,网络爬虫,但是基本都是用什么python或者JAVA写,貌似很少看到用c++写的,我在网上找了一个,看到其实还是很简单的算法 算法讲解:1.遍历资源网站 2.获取html信息   ...

  2. 老李分享:robotium3.6与4.0 later 的区别 2

    再仔细看了下4.0中的方法:  java.util.ArrayList<android.view.View> getCurrentViews()           Returns an ...

  3. 老李案例分享:MAT分析应用程序服务出现内存溢出过程

    老李案例分享:MAT分析应用程序服务出现内存溢出过程   poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.在poptest的loa ...

  4. java中的==、equals()、hashCode()源码分析

    转载自:http://www.cnblogs.com/xudong-bupt/p/3960177.html 在Java编程或者面试中经常会遇到 == .equals()的比较.自己看了看源码,结合实际 ...

  5. 提交到SVN中的项目被删除 且项目名已经被新建项目占用找回方法

    提到项目找回,一看就头疼,找回起来较麻烦.下面就讲一下. 首先,确定项目是否被删除?找项目,太多了,都被找一遍了,还是没找到,看看就头痛,换了个方法,找了个项目的包,xx.apk,反编译下吧,过程略, ...

  6. 使用FSharp 探索Dotnet图像处理功能2--均衡灰度

    重新捡起大学里的图像处理,好像之前什么都没学到,但是我为什么还留着这本书呢?嘿嘿. 看到均衡灰度处理,上来就是积分,概率分布的公式,头微微的有点疼.网上看了点介绍,隔天再拿起书本,总算有了点眉目.简而 ...

  7. sleep()和wait()的区别 --- 快入睡了,突然想起一个知识点,搞完就睡

    自从开了博客之后,大部分是转发的,不断的ctrl+c和ctrl+v,知识是越屯越多,吸收的却很少,后来越来越懒,直接保存链接了. 我已经认识到了自己的错误,自己查询到的这些知识,以后还是会定期保存的, ...

  8. JDBC基础学习(六)—数据库连接池

    一.数据库连接池介绍 1.数据库连接池的缘由      对于一个简单的数据库应用,由于对于数据库的访问不是很频繁.这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什 ...

  9. linux下如何查看mysql、apache是否安装,并卸载

    --linux下如何查看mysql.apache是否安装,并卸载? http://blog.163.com/dengxiuhua126@126/blog/static/1186077720137311 ...

  10. android studio 2.3 下载地址

      android studio下载:  Windows+SDK:(1.8GB)| Windows(428 MB) | Linux    idea  win.exe  win.zip 序号 名称 中文 ...