SQL Server 2005技术内幕:查询、调整和优化4——聚合
对于聚合,SQL Server支持两种物理操作符:流聚合与哈希聚合。
标量聚合
标量聚合通过在选择列表中没有GROUP BY语句的聚合函数查询。标量聚合总是返回一个单一的行。SQL Server总是使用流聚合操作符实现标量聚合。
下面是个简单的例子:
这个查询产生下面的计划:
流聚合操作符就是计算输入行的大小并返回结果。流聚合实际计算Bigint的总数([Expro1004])。计算的数量需要把这个结果转换成期待的Int型。注意到标量流聚合是一个没有页节点操作符例子中的一个,可以在空输入集时产生一个输入。
如何实现其他的简单标量聚合函数,例如MIN、MAX和SUM。一个单一流聚合操作符可以在同一时刻计算多个标量聚合。
下面是使用单一流聚合操作符的查询计划:
注意到SQL Server不需要对MIN和MAX集合转换结果,因为这些集合是基于订单日期列数据类型计算的。
一些集合,例如AVG实际上是从其他两个集合如SUM和COUNT中计算的:
请注意计划中的计算标量操作符是如何从sum和count中计算平均值的:
CASE表达式是必须的,用以确定SQL Server不会试图被零整除。
尽管本质上SUM不需要计算,但仍然要进行计数:
以下这个查询计划中的CASE表达式如果应用COUNT来确保SUM在没有行时返回NULL,而不是零。
Scalar Distinct
如果对标量聚合添加DISTINCT关键字进行查询,计算以运送的订单的不同城市的数量,查询如下:
以上查询产生的查询计划如下:
因为查询必须对那些ShipCity列上有唯一值的行计算,SQL Server 增加显示排序操作符消除ShipCity上的重复值,显示排序是SQL Server用来消除排序重复行的常见方法之一。在对输入集进行排序后就很容易去除重复的行,因为重复的行和另外一个行是邻接的。
多个 Distinct
from [orders]
SQL Server可以通过消除ShipAddress列上重复值的行来计算COUNT(DISTINCT[ShipAddress])。类似的,SQL Server可以通过消除ShipCity列上的重复值来计算COUNT(DISTINCT[ShipTity])。但是,假设给出的这两个行的集合不同,SQL Server如果同时对这两个行进行计算,SQL Server做不到,它必须先计算一个聚合的结果,然后计算另一个,再把这两个结果合并成一个单一的输出:
嵌套循环连接的两个输入从原始的查询中计算两个数量,其中一个输入去掉重复的,计算ShipAddress列的数量,另一个输入去掉重复的,计算ShipCity列的数量。嵌套循环连接没有连接谓词,它是交叉连接。因为嵌套循环连接的两个输入每一个产生单一的行,且它们的标量集合-交叉连接的结果也会一个单一的行。交叉连接就是把结果的两行“粘贴”为单一的行。
流聚合
算法 流聚合依赖与获得存储在GROUP BY列中的数据。如同合并连接,如果一个查询包含GROUP BY语句多于一行,流聚合会使用任何包含所有行的排序次序。例如,流聚合会对Col1列和Col2列分组,而数据存储在(Col1,Col2)或(Col2,Col1)上。与合并连接类似,排序次序也会被一个索引或一个显示排序操作符影响。排序次序将确保列GROUP BY有着相同值的行集合彼此相连。
例如,为了计算SUM,流聚合会考虑每个输入行。如果输入行属于目前的组(输入行的group by列和以前行的group by列是匹配的),则流聚合可通过从输入行中增加合适的值来更新目前的SUM。如果输入的行属于一个新的组(输入的行group by列和以前行的group by列不匹配),流聚合会输出目前的SUM,把SUM设为零,开始新的组。
简单示例 下面的查询将计算对于每个地址运送订单的数量:
From [Orders] group by [ShipAddress], [ShipCity]
下面是这个查询的计划:
除了SQL Server在聚合前需要对数据排序外,这个计划和之前看到的标量聚合的查询基础一样,可以认为标量聚合是一个包含所有行的一个大组,因此,对于一个标量聚合,没有必要将行排序为不同的组。(未完待续)