sql Server如何实现group by子句(聚合)?
作为灵感,采取this question’s查询的执行计划:
select p_id,DATEDIFF(D,MIN(TreatmentDate),MAX(TreatmentDate)) from patientsTable group by p_id
在查询数据之前,简单的select语句及其执行计划是这样的:
使用查询和执行计划检索数据后:
通常它是Stream Aggregate或Hash Aggregate.
> Stream aggregate对结果集进行排序,扫描并返回每个新值(不等于扫描中的最后一个值).它允许保留一组聚合状态变量.
>哈希聚合从结果集构建哈希表.每个条目保留聚合状态变量,这些变量在散列未命中时初始化并在散列命中时更新.
让我们看看AVG是如何工作的.它需要两个状态变量:sum和count
grouper value 1 4 1 3 2 8 1 7 2 1 1 2 2 6 2 3
流聚合
>首先,它需要对值进行排序:
grouper value 1 4 1 3 1 7 1 2 2 8 2 1 2 6 2 3
>然后,它保留一组状态变量,初始化为0,并扫描排序的结果集:
石斑鱼价值总和
– 进入
– 变量:0 0
1 4 4 1
1 3 7 2
1 7 14 3
1 2 16 4
– 集团变更.返回结果并重新初始化变量
– 返回1,4
– 变量:0 0
2 8 8 1
2 1 9 2
2 6 15 3
2 3 18 4
– 集团变更.返回结果并重新初始化变量
– 返回2,4.5
– 结束
哈希聚合
>只需扫描值并将状态变量保留在哈希表中:
grouper value -- Hash miss. Adding new entry to the hash table -- [1] (0,0) -- ... and updating it: 1 4 [1] (4,1) -- Hash hit. Updating the entry: 1 3 [1] (7,2) -- Hash miss. Adding new entry to the hash table -- [1] (7,2) [2] (0,0) -- ... and updating it: 2 8 [1] (7,2) [2] (8,1) 1 7 [1] (14,3) [2] (8,1) 2 1 [1] (14,3) [2] (9,2) 1 2 [1] (16,4) [2] (9,2) 2 6 [1] (16,4) [2] (15,3) 2 3 [1] (16,4) [2] (18,4) -- Scanning the hash table and returning the aggregated values -- 1 4 -- 2 4.5
通常,如果结果集已经排序,则排序更快(例如,值来自索引或由前一操作排序的结果集).
散列更快是结果集未排序(散列比排序更快).
MIN和MAX是特殊情况,因为它们不需要扫描整个组:只有组内聚合列的第一个和最后一个值.
不幸的是,与大多数其他系统不同,sql Server无法有效利用它,因为它不适合进行INDEX SKIP SCAN(跳过不同的索引键).
虽然简单的MAX和MIN(没有GROUP BY子句)如果聚合列上的索引存在,则使用TOP方法,而带有GROUP BY的MIN和MAX使用与其他聚合函数相同的方法.