聚合(分组)如何在SQL Server上工作?

uza*_*y95 7 sql sql-server

SQL Server如何实现group by子句(聚合)?

作为灵感,采取这个问题的查询的执行计划:

select p_id, DATEDIFF(D, MIN(TreatmentDate), MAX(TreatmentDate)) from 
patientsTable group by p_id
Run Code Online (Sandbox Code Playgroud)

在查询数据之前,简单的select语句及其执行计划是这样的: 替代文字

使用查询和执行计划检索数据后: 替代文字

Qua*_*noi 22

通常它是a Stream Aggregate或a Hash Aggregate.

  • Stream aggregate对结果集进行排序,扫描并返回每个新值(不等于扫描中的最后一个值).它允许保留一组聚合状态变量.

  • Hash aggregate从结果集构建哈希表.每个条目保留聚合状态变量,这些变量在散列未命中时初始化并在散列命中时更新.

让我们看看它是如何AVG运作的.它需要两个状态变量:sumcount

grouper  value
1        4
1        3
2        8
1        7
2        1
1        2
2        6
2        3
Run Code Online (Sandbox Code Playgroud)

流聚合

  1. 首先,它需要对值进行排序:

    grouper  value
    1        4
    1        3
    1        7
    1        2
    2        8
    2        1
    2        6
    2        3
    
    Run Code Online (Sandbox Code Playgroud)
    • 然后,它保留一组状态变量,初始化为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
    
    Run Code Online (Sandbox Code Playgroud)

通常,如果结果集已经排序,则排序更快(例如,值来自索引或由前一操作排序的结果集).

散列更快是结果集未排序(散列比排序更快).

MIN并且MAX是特殊情况,因为它们不需要扫描整个组:只有组内聚合列的第一个和最后一个值.

不幸的是,SQL Server与大多数其他系统不同,它不能有效地利用它,因为它做得不好INDEX SKIP SCAN(跳过不同的索引键).

而简单MAXMIN(没有GROUP BY子句)使用TOP方法,如果所聚集的列的索引的存在,MINMAXGROUP BY使用相同的方法,其它集合函数做.