SQL Server 2005技术内幕:查询、调整和优化4——聚合

对于聚合,SQL Server支持两种物理操作符:流聚合与哈希聚合。

 

标量聚合

标量聚合通过在选择列表中没有GROUP BY语句的聚合函数查询。标量聚合总是返回一个单一的行。SQL Server总是使用流聚合操作符实现标量聚合。

下面是个简单的例子:

 

select COUNT(*from [Orders]

 

 

这个查询产生下面的计划:

 

流聚合操作符就是计算输入行的大小并返回结果。流聚合实际计算Bigint的总数([Expro1004])。计算的数量需要把这个结果转换成期待的Int型。注意到标量流聚合是一个没有页节点操作符例子中的一个,可以在空输入集时产生一个输入。

 

如何实现其他的简单标量聚合函数,例如MINMAXSUM。一个单一流聚合操作符可以在同一时刻计算多个标量聚合。

 

select MIN(Orderdate), MAX(orderdate) from [Orders]

 

 

下面是使用单一流聚合操作符的查询计划:

 

注意到SQL Server不需要对MINMAX集合转换结果,因为这些集合是基于订单日期列数据类型计算的。

一些集合,例如AVG实际上是从其他两个集合如SUMCOUNT中计算的:

 

select AVG(Freight) from [orders]

 

 

请注意计划中的计算标量操作符是如何从sumcount中计算平均值的:

 

 

CASE表达式是必须的,用以确定SQL Server不会试图被零整除。

        

select SUM(Freight) from [orders]

 

        

   尽管本质上SUM不需要计算,但仍然要进行计数:

         以下这个查询计划中的CASE表达式如果应用COUNT来确保SUM在没有行时返回NULL,而不是零。

 

Scalar Distinct

         如果对标量聚合添加DISTINCT关键字进行查询,计算以运送的订单的不同城市的数量,查询如下:

 

select COUNT(distinct [shipcity]from [orders]

 

 

以上查询产生的查询计划如下:

 

因为查询必须对那些ShipCity列上有唯一值的行计算,SQL Server 增加显示排序操作符消除ShipCity上的重复值,显示排序是SQL Server用来消除排序重复行的常见方法之一。在对输入集进行排序后就很容易去除重复的行,因为重复的行和另外一个行是邻接的。

 

多个 Distinct

 

select COUNT(distinct [shipaddress]), COUNT(distinct[shipcity]

   
from [orders]

 

 

SQL Server可以通过消除ShipAddress列上重复值的行来计算COUNTDISTINCT[ShipAddress])。类似的,SQL Server可以通过消除ShipCity列上的重复值来计算COUNTDISTINCT[ShipTity])。但是,假设给出的这两个行的集合不同,SQL Server如果同时对这两个行进行计算,SQL Server做不到,它必须先计算一个聚合的结果,然后计算另一个,再把这两个结果合并成一个单一的输出:

 

嵌套循环连接的两个输入从原始的查询中计算两个数量,其中一个输入去掉重复的,计算ShipAddress列的数量,另一个输入去掉重复的,计算ShipCity列的数量。嵌套循环连接没有连接谓词,它是交叉连接。因为嵌套循环连接的两个输入每一个产生单一的行,且它们的标量集合-交叉连接的结果也会一个单一的行。交叉连接就是把结果的两行“粘贴”为单一的行。

 

流聚合

 

算法         流聚合依赖与获得存储在GROUP BY列中的数据。如同合并连接,如果一个查询包含GROUP BY语句多于一行,流聚合会使用任何包含所有行的排序次序。例如,流聚合会对Col1列和Col2列分组,而数据存储在(Col1Col2)或(Col2Col1)上。与合并连接类似,排序次序也会被一个索引或一个显示排序操作符影响。排序次序将确保列GROUP BY有着相同值的行集合彼此相连。

 

例如,为了计算SUM,流聚合会考虑每个输入行。如果输入行属于目前的组(输入行的group by列和以前行的group by列是匹配的),则流聚合可通过从输入行中增加合适的值来更新目前的SUM。如果输入的行属于一个新的组(输入的行group by列和以前行的group by列不匹配),流聚合会输出目前的SUM,把SUM设为零,开始新的组。

 

简单示例  下面的查询将计算对于每个地址运送订单的数量:

 

select [ShipAddress][ShipCity]COUNT(*)

From [Orders] group by [ShipAddress][ShipCity]

 

 

下面是这个查询的计划:

 

除了SQL Server在聚合前需要对数据排序外,这个计划和之前看到的标量聚合的查询基础一样,可以认为标量聚合是一个包含所有行的一个大组,因此,对于一个标量聚合,没有必要将行排序为不同的组。(未完待续)

posted @ 2010-09-02 15:26  冯翔  阅读(1025)  评论(0编辑  收藏  举报