web-dev-qa-db-ja.com

SQL Server-パフォーマンスが重要な場合は、各グループから最新のレコードを選択します

SQL Server 2016データベースを実行していますが、次の表には1億を超える行があります。

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

このテーブルには、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 

現在実行しているのは次のクエリで、実行に約90〜120秒かかります。

    SELECT StationId, ParameterId, MAX(DateTime) AS LastDate
    FROM  MyTable WITH (NOLOCK)
    GROUP BY StationId, ParameterId

私はまた、以下を提案する多くの投稿を見てきました。実行には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

最良の場合でも(GROUP BY句とMAX集計関数を使用)、実行プランはインデックスシークを示しません。

Query execution plan

Index Scan detail

より良い実行時間を達成するために、このクエリを実行する(またはインデックスを構築する)より良い方法があるかどうか疑問に思います。

(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

SQL Serverがルックアップを実行できるようにするには、各(StationID、ParameterID)ペアの最新のDateTimeを探します。

(StationID、ParameterID、DateTime)のクラスター化インデックスのみでは、SQL Serverがインデックスのリーフレベルをスキャンせずに個別の(StationID、ParameterID)ペアを検出する方法はなく、スキャン中に最大のDateTimeを見つけることができます。

また、100M以上の行では、このテーブルは、BTreeクラスター化インデックスよりもクラスター化列ストアとして優れている場合があります。

パフォーマンスが非常に重要で、テーブルに最新の日付を頻繁に要求する場合...最新のタイムスタンプを持つキーとしてステーションとパラメーターのルックアップテーブルを作成してみませんか。大きなテーブルを変更するたびにこのテーブルを更新する必要がありますが、この方法では、必要なときに結果をミリ秒で取得できます。

0
Taczi

CTEのrow_numberアプローチでは、StationId, ParameterId, DateTime descに非クラスター化インデックスを作成してみてください。 partition by order句が使用する適切なソート順のインデックスがあると、パフォーマンスが向上することがわかりました。

0
Connor