SQL Server - 当性能至关重要时从每个组中选择最近的记录

Ped*_*ini 19 index sql-server optimization greatest-n-per-group sql-server-2016

我运行了一个 SQL Server 2016 数据库,其中有一个包含 100 多万行的下表:

StationId | ParameterId |       DateTime       | Value
    1     |      2      | 2020-02-04 15:00:000 |  5.20
    1     |      2      | 2020-02-04 14:00:000 |  5.20
    1     |      2      | 2020-02-04 13:00:000 |  5.20
    1     |      3      | 2020-02-04 15:00:000 |  2.81
    1     |      3      | 2020-02-04 14:00:000 |  2.81
    1     |      4      | 2020-02-04 15:00:000 |  5.23
    2     |      2      | 2020-02-04 15:00:000 |  3.70
    2     |      4      | 2020-02-04 15:00:000 |  12.20
    3     |      2      | 2020-02-04 15:00:000 |  1.10
Run Code Online (Sandbox Code Playgroud)

该表具有 StationId、ParameterId 和 DateTime 的聚集索引,按此顺序,全部升序。

我需要的是,对于每个唯一的对 StationId - ParameterId,从 DateTime 列返回最新值:

StationId | ParameterId |       LastDate       
    1     |      2      | 2020-02-04 15:00:000 
    1     |      3      | 2020-02-04 15:00:000 
    1     |      4      | 2020-02-04 15:00:000 
    2     |      2      | 2020-02-04 15:00:000 
    2     |      4      | 2020-02-04 15:00:000 
    3     |      2      | 2020-02-04 15:00:000 
Run Code Online (Sandbox Code Playgroud)

我现在正在做的是以下查询,运行大约需要 90 到 120 秒:

    SELECT StationId, ParameterId, MAX(DateTime) AS LastDate
    FROM  MyTable WITH (NOLOCK)
    GROUP BY StationId, ParameterId
Run Code Online (Sandbox Code Playgroud)

我还看到许多帖子提出以下建议,运行时间超过 10 分钟:

    SELECT StationId, ParameterId, DateTime AS LastDate
    FROM
    (
       SELECT StationId, ParameterId, DateTime
       ,ROW_NUMBER() OVER (PARTITION BY StationId,ParameterIdORDER BY DateTime DESC) as row_num
       FROM  MyTable WITH (NOLOCK)
    )
    WHERE row_num = 1
Run Code Online (Sandbox Code Playgroud)

即使在最好的情况下(使用 GROUP BY 子句和 MAX 聚合函数),执行计划也不指示索引查找:

查询执行计划

索引扫描细节

我想知道是否有更好的方法来执行此查询(或构建索引)以实现更好的执行时间。

Dav*_*oft 25

如果您有足够少的 (StationID, ParameterID) 对,请尝试如下查询:

select StationID, ParameterID, m.DateTime LastDate 
from StationParameter sp
cross apply 
  (
     select top 1 DateTime 
     from MyTable 
     where StationID = sp.StationID
      and ParameterID = sp.ParameterID
     order by DateTime desc
  ) m
Run Code Online (Sandbox Code Playgroud)

为了使 SQL Server 能够执行查找,DateTime为每个 (StationID,ParameterID) 对寻找最新的。

只有在 (StationID, ParameterID, DateTime) 上的聚集索引,SQL Server 无法在不扫描索引的叶级别的情况下发现不同的 (StationID, ParameterID) 对,并且它可以在扫描时找到最大的 DateTime。

同样在 100M+ 行时,这个表可能更适合作为聚集列存储而不是 BTree 聚集索引。

  • (StationID, ParameterID) 对的数量约为 300。现在正在执行索引查找,执行时间根据您的建议降至 1 秒以下 (4认同)