前言:本系列文章不知不觉已经到了第十篇了。有朋友一直坚持看本系列,非常感谢。有朋友问我,看是看了,貌似用不着啊。到现在为止,确实用不上,但是,这是明白查询化优化器的必经之路,也是学会查看执行计划的必备知识,更是以后性能调优的基本技能,厚积薄发。
相信对于聚合操作,大家应该是非常的熟悉了。在数据库中,我们常常采用聚合操作来对一批数据进行处理,例如SUM,AVG or MAX,Group By等。我们查询中的聚合操作,属于逻辑操作,在数据库底层,这些逻辑的聚合操作是有两类不同的物理操作来实现的:Stream Aggregate 和 Hash Aggregate。
在真正开始讲述这两个物理操作之前,我这里首先介绍一下排序(Sort)和哈希(Hash)。这两个操作非常重要,例如,如果要进行Stream Aggregate操作,那么就要先保证数据是排序了的。排序的操作是非常消耗内存的,如果内存不足,就会使用tempdb数据来存放数据,从而使得磁盘的读写操作变多和cpu的使用变高。另外,对于没有排序的数据,在聚合的时候,就会采用哈希表的形式,关于这一点,我们在后面讲述Join的时候会进一步的讲述。
总是记住一点就是:如果数据是排序的了,那么聚合的物理操作就会选择Stream Aggregate,那么Join的物理操作就会使用Merge Join;如果数据没有排序,那么聚合就会选项Hash Aggregate,Join就会选择Hash Join。如果我们想在在没有排序的数据上面使用“在排序数据上面才有的操作”,那么数据就会被先排序(被我们或者被数据库)。
Stream Aggregate
还是像之前一样,我们通过一个例子来说明,如下:
执行计划如下:
为了理解上面的执行计划,这里为大家普及一个概念:如果一个查询只使用了聚合函数,同时没有使用Group By,那么这个聚合就称为scalar 聚合,就是说,这个聚合只返回一个值,那么这个聚合的物理操作总是用Stream Aggregate来实现的。对于这个“scalar”的理解,大家可以联想一下ADO.NET中的sqlCommand的一个操作:ExecuteScalar(),返回一个值。
上面的执行计划中使用了Stream Aggregate操作吗,为了更好的查看相关的信息,我们来看看这个查询的文本的执行计划,运行如下查询:
文本执行计划如下:
大家可以看到:在AVG聚合函数执行的时候,Stream Aggregate操作使用了Count和SUM聚合函数,把这两个聚合函数的结果分别放在Expr1004
和Expr1005中,Compute Scalar操作验证做了一个除数是否为零的操作,之后就进行一个转换数据类型的操作,最后就执行运算。
我们再看一个例子,如下:
通过查看执行计划,发现有点怪,如下:
发现没有Sort操作,为什么?
原来SalesOrderID是SalesOrderDetail的聚集索引,也就是说,数据已经是排序的了,所以这里没有Sort操作,并且SUM的聚合操作选用了Stream Aggregate实现。
看到这里,大家心里可能就想了:怎么都是Stream Aggregate?什么意思?看到现在没有什么感觉。
下面我们就来看看 hash Aggregate,看完了之后,两者结合理解,就差不多了。
Hash Aggregate
查询优化器在一个很大的并且数据没有排序的表上面为聚合操作选择Hash Aggregate。我们来看下面的一个查询,如下:
执行计划如下:
在上面的查询中需要根据ContactID来对整个表的数据进行分组的统计,又因为这个表中有3万多条数据,所以对于大量无序数据的一些操作,基本都是采用在内存中建立哈希表的方式来处理。后面我们讲到的Hash Join也是这样的。
下面,我们再来看另外一个查询,如下:
执行计划如下:
@H_404_689@ 20120820164449.png(16.24 K)