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)
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的性能测试的更多相关文章
- SQL Server中Table字典数据的查询SQL示例代码
SQL Server中Table字典数据的查询SQL示例代码 前言 在数据库系统原理与设计(第3版)教科书中这样写道: 数据库包含4类数据: 1.用户数据 2.元数据 3.索引 4.应用元数据 其中, ...
- SQL Server进阶(五)子查询
概述 子查询的概念: 当一个查询是另一个查询的条件时,称之为子查询.子查询可以嵌套在主查询中所有位置,包括SELECT.FROM.WHERE.GROUP BY.HAVING.ORDER BY. 外面的 ...
- SQL SERVER技术内幕之4 子查询
最外层查询的结果集会返回给调用者,称为外部查询.内部查询的结果是供外部查询使用的,也称为子查询.子查询可以分成独立子查询和相关子查询两类.独立子查询不依赖于它所属的外部查询,而相关子查询则须依赖它所属 ...
- SQL Server中一些不常见的查询
把一些不常见但又会用到的SQL查询整理备份一下 --筛选出某个字段中包含中文的记录 SELECT * FROM temp WHERE W1 LIKE '%[吖-座]%' --筛选出某个字段在哪些表中存 ...
- SQL SERVER中生僻字问题存储与查询问题
以下仅记录碰到的几个问题 1.首先字段设置为varchar的时候存储后无法进行正常的显示 显示为? 此状态下匹配查询或者Like模糊查询都没问题 2.将字段设置为nvarchar,在进行插入或者跟新时 ...
- sql server中数据约束相关的查询
根据表名查找数据约束 SELECT * FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_NAME = 'CMS_EventLog'; SEL ...
- SQL SERVER中XML查询:FOR XML指定PATH
SQL SERVER中XML查询:FOR XML指定PATH 前言 在SQL SERVER中,XML查询能够指定RAW,AUTO,EXPLICIT,PATH.本文用一些实例介绍SQL SERVER中指 ...
- 在SQL Server中为什么不建议使用Not In子查询
在SQL Server中,子查询可以分为相关子查询和无关子查询,对于无关子查询来说,Not In子句比较常见,但Not In潜在会带来下面两种问题: 结果不准确 查询性能低下 下面 ...
- (网页)在SQL Server中为什么不建议使用Not In子查询(转)
转自博客园宋沄剑 英文名:CareySon : 在SQL Server中,子查询可以分为相关子查询和无关子查询,对于无关子查询来说,Not In子句比较常见,但Not In潜在会带来下面两种问题: ...
随机推荐
- as3中去掉字符串两边的空格,换行符
as3 去掉字符串两边的空格,换行符,方法一 ActionScript Code 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 pub ...
- poptest老李谈分布式与集群 2
集群分类 Linux集群主要分成三大类( 高可用集群, 负载均衡集群,科学计算集群) 高可用集群( High Availability Cluster)负载均衡集群(Load Balance Clus ...
- 手机自动化测试:Appium代码之Logger
手机自动化测试:Appium代码之Logger poptest是国内唯一一家培养测试开发工程师的培训机构,以学员能胜任自动化测试,性能测试,测试工具开发等工作为目标.poptest推出手机自动化测 ...
- 6.Redis常用命令:Set
在Redis中,我们可以将Set类型看作为没有排序的字符集合,和List类型一样,我们也可以在该类型的数据值上执行添加.删除或判断某一元素是否存在等操作.需要说明的是,这些操作的时间复杂度为O(1), ...
- C语言编码风格_集锦_1
参考原地址: http://www.jb51.net/article/79257.htm <一> 在一个标准的C语言程序中, 最特殊的莫过于main函数了. 函数大体上分为内联函数(C99 ...
- 采用Spring AOP+Log4j记录项目日志
转载请注明出处:http://www.cnblogs.com/Joanna-Yan/p/6567672.html 项目日志记录是项目开发.运营必不可少的内容,有了它可以对系统有整体的把控,出现任何问题 ...
- 使用SharedPreferences存储用户配置信息
用SharedPreferences来保存用户的基本配置信息非常的方便,实现起来也很容易:以下是一个简单的例子: 效果截图: 主要代码: public class MainActivity ex ...
- 应用程序写Xml文档
主要用到CreateElement.CreateTextNode.CreateComment.AppendChild.InsertAfter方法 代码如下: XmlDocument document ...
- seo从业者发展方向
对于很多朋友来说,seo就是一项比较简单的技能,内容+外链,就可以基本囊括seo的基本内容了.可能很多朋友对此不屑一顾,会说seo可是包含万象, 你需要懂网页设计.标签设计,分词优化.企业建站等等方面 ...
- CTR预估中的贝叶斯平滑方法(二)参数估计和代码实现
1. 前言 前面博客介绍了CTR预估中的贝叶斯平滑方法的原理http://www.cnblogs.com/bentuwuying/p/6389222.html. 这篇博客主要是介绍如何对贝叶斯平滑的参 ...