本节咱们来分析LEFT JOIN和NOT EXISTS,简短的内容,深刻的理解,Always to review the basics。sql
以前咱们已经分析过IN查询在处理空值时是基于三值逻辑,只要子查询中存在空值此时则没有任何数据返回,而LEFT JOIN和NOT EXISTS不管子查询中有无空值上处理都是同样的,固然比较重要的是利用LEFT JOIN...IS NULL来检查NULL。基于两者返回的结果集是同样的,下面咱们开始直接用前面节所建立表来进行测试。在BigTable和SmallerTable上首先未建立索引性能
USE TSQL2012 GO DBCC FREEPROCCACHE DBCC DROPCLEANBUFFERS SET STATISTICS IO ON SET STATISTICS TIME ON SELECT BigTable.ID, SomeColumn FROM BigTable LEFT OUTER JOIN SmallerTable ON BigTable.SomeColumn = SmallerTable.LookupColumn WHERE LookupColumn IS NULL SELECT ID, SomeColumn FROM BigTable WHERE NOT EXISTS (SELECT LookupColumn FROM SmallerTable WHERE SmallerTable.LookupColumn = BigTable.SomeColumn)
两者执行CPU Time和elapsed Time以下学习
咱们看到上述查询计划未建立索引以前两者在开销上接近一致,而LEFT JOIN....IS NULL则首先进行哈希匹配中的右外部联接,而后就是过滤,换句话说是LEFT JOIN....IS NULL会直接彻底JOIN,而后再对重复数据进行过滤,而NOT EXISTS则是直接利用哈希匹配中的右半联接,关于半联接咱们在前面也已经说过,此时如有重复数据直接只取一个。因此LEFT JOIN....IS NULL和NOT EXISTS两者对于重复数据一个经过两部操做完成先彻底JOIN后进行过滤,而另一个则是直接经过右半联接过滤。因此对于此两者最大的不一样在于:当使用LEFT JOIN.....IS NULL时,SQL尚未那么聪明,仅仅只检查一次,所以它须要经过彻底JOIN和过滤来完成,而NOT EXISTS则是在JOIN时就进行过滤。测试
在看两者执行CPU TIME和elapsed TIME时间,没有太大的差别。接下来咱们再来建立索引看看。优化
CREATE INDEX idx_BigTable_SomeColumn
ON BigTable (SomeColumn)
CREATE INDEX idx_SmallerTable_LookupColumn
ON SmallerTable (LookupColumn)
看看两者的查询执行计划spa
此时咱们经过看到上述查询执行计划,咱们可以清楚的看到LEFT JOIN....IS NULL仍是彻底JOIN而后在过滤,只是建立了索引以后性能改善了一点而已,可是不一样于LEFT JOIN...IS NULL的NOT EXISTS的计划执行状况不一样于未建立索引,此时首先利用了流聚合而后哈希匹配中的右半联接变成了合并联接中的右半联接,咱们一个个来看,这个Stream Aggregate(流聚合)是什么鬼,对于此流聚合我是不了解的,不能装懂,咱们接下来具体讲讲流聚合,至于为何每当查询计划出现一个新的名词都要去详细了解下的缘由,相信看过我SQL Server本系列的童鞋知道,每一节的内容都很是短,不会出现阅读疲劳,并且是精讲,我重头系统学习SQL Server是为了对SQL Server中全部涉及到对性能调优有关的地方以及一些基础知识都会去过一遍,以便后续再出现性能调优不至于一筹莫展。好了,回到话题,咱们看看Stream Aggregate。code
msdn上有关概念以下:Stream Aggregate运算符按一列或多列对行分组,而后计算查询返回的一个或多个聚合表达式。此运算符的输出可供查询中的后续运算符引用和/或返回到客户端。Stream Aggregate 运算符要求输入在组中按列进行排序。若是因为前面的 Sort 运算符或已排序的索引查找或扫描致使数据还没有排序,优化器将在此运算符前面使用一个 Sort 运算符。在 SHOWPLAN_ALL 语句或 SQL Server Management Studio 的图形执行计划中,GROUP BY 谓词中的列会列在 Argument 列中,而聚合表达式列在 Defined Values 列中。 blog
经过上述定义仅仅只是知道Stream Aggregate是用对行或者列进行聚合,至于何时在查询计划中出现流聚合,何时利用流聚合来提升查询性能都是不得而知,咱们接下来一块儿探讨下。上述着重在于【分组】而后进行【聚合】计算,基于这点咱们来看看使用Stream Aggregate的三种场景。排序
USE TSQL2012
GO
SELECT COUNT(custid) AS cutid, SUM(empid) AS empid
FROM Sales.Orders
USE TSQL2012
GO
SELECT custid, COUNT(custid) AS countCustId
FROM Sales.Orders
GROUP BY custid
USE TSQL2012
GO
SELECT DISTINCT custid
FROM Sales.Orders
上述查询使用经过DISTINCT,其实是对cutid进行了分组。以上是用到了Stream Aggregate的场景,固然聚合还有另一种就是哈希匹配聚合,后续会再进行补充。咱们再来理解Stream Aggregate定义,咱们将定义归纳为对输入进行排序后,接下来进行分组而后再进行聚合计算。在上述(2)和(3)中都是进行了分组,可是没有排序,实际上内部已经默认实现了排序,咱们看下在(3)中表中custid数据,以下索引
当进行DISTINCT以后
可是在(3)中没有进行聚合,为何会进行流聚合呢?实际上在流聚合中存在状态变量,状态变量具体个数根据聚合个数而定,此状态变量用来设置结果集,当进行分组后对应的数据进行保存,此时对应的状态变量为0,当匹配到对应数据时此时状态变量加1,因此上述(3)中能够说隐式进行了聚合计算,只是每条数据对应的状态变量为0而已,到了这里就不难解释,只进行了排序,分组而没有进行聚合计算的缘由。关于Stream Aggregate都知道的一个例子则是咱们在利用SqlDataReader记性读取数据时,能够说是读取流记录,若是咱们须要汇总结果集时,此时每当Read时,其内部的状态变量都会加1最终返回汇总和到客户端。在这里咱们只是简单讲讲Stream Aggregate,后续会一并讲讲Hash Aggregate。咱们继续回到LEFT JOIN....IS NULL和NOT EXISTS话题,当咱们建立索引以后此时LEFT JOIN....IS ISNULL执行时间是NOT EXISITS的两倍多。到此,关于LEFT JOIN...IS NULL和NOT EXISTS就此结束,咱们一样下个基本结论。
LEFT JOIN...IS NULL和NOT EXISTS性能分析结论:当咱们须要找到子查询中不匹配的行而且列为可空时,此时用NOT EXISTS,当须要找到子查询中不匹配的行,此时列不为空时能够用NOT EXISTS或者NOT IN。
因为LEFT JOIN..IS NULL对于不匹配的行不会当即进行返回而先须要彻底JOIN后过滤,尤为是当有多个条件时,LEFT JOIN...IS NULL可能会更加影响查询性能。
本节咱们学习了LEFT JOIN..IS NULL和NOT EXISTS的性能分析,下节咱们进入这几节内容的综合篇,综合比较NOT IN VS NOT EXISTS VS LEFT JOIN...IS NULL终极篇。简短的内容,深刻的理解,咱们下节再会。