java join in_SQL Server中INNER JOIN与子查询IN的性能测试

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

1372135ab17ddfc26f2a041535efbc30.png

cf6b482fcf7b22ef1f5606d7e9226403.png

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行数据),两者在逻辑上实际上是不一致的。因为重复数据的缘故。撇开这个不谈,光从性能上来考察两种,它们几乎是一模一样。没有优劣之分。

0f2a5dcb4876e4124f8a82c7be92bc26.png

1cec6e93130acc1766fabca4498d4623.png

如果有人对上面的重复数据不明白的话,下面做个简单的例子演示给大家看看。如下所示,截图中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'

722dc38f45b41c6bff9add986e628c9e.png

其实下面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;

eaacfbd6f4bfca0e9a91dc510a5aba31.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 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要好。

bac74557fae230575e9be73c4d2dd7c8.png

0d1194c9416cd0ae31409f96ff3e66a8.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 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)

1b03ff2cfe176c30c2cef3e45a27681b.png

3e2fd4f5bcf7d2ba373217ef88d00d41.png

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

USE [AdventureWorks2014]

GO

CREATE NONCLUSTERED INDEX [IX_C_N1]

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

INCLUDE ([C_ID],[COLN])

GO

63f97ae2dcec3c42d3330b22f8966e17.png

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

c5d14e78dc047dc73428f61f199afd04.png

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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值