web-dev-qa-db-fra.com

SQL Server - Sélectionnez le dernier enregistrement récent de chaque groupe lorsque la performance est critique

Je gère une base de données SQL Server 2016 où j'ai la table suivante avec 100 lignes de plus de plus de millions:

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

Cette table a un index en cluster pour StationID, ParameID et DateTime, dans cet ordre, tous ascendants.

Ce dont j'ai besoin, c'est que pour chaque paire unique Stationid - paramétrice, renvoyez la valeur la plus récente de la colonne 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 

Ce que je fais maintenant, c'est la requête suivante, qui prend environ 90 à 120 secondes pour courir:

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

J'ai également vu de nombreux postes suggérant ce qui suit, qui prend plus de 10 minutes à courir:

    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

Même dans le meilleur cas (en utilisant le groupe par la clause et la fonction d'agrégation maximale), le plan d'exécution n'indique pas un indice de recherche:

Query execution plan

Index Scan detail

Je me demande s'il y a un meilleur moyen d'effectuer cette requête (ou de construire l'index) afin d'obtenir une meilleure heure d'exécution.

17

Si vous avez un petit nombre suffisant de paires (stationid, paramétrice), essayez une requête comme celle-ci:

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

Pour activer SQL Server d'effectuer une recherche, recherchez le dernier DateTime pour chaque paire (stationId, paramètre).

Avec seulement un indice en cluster sur (stationId, paramétrice, DateTime), SQL Server découvre les paires distinctes (stationidales, paramétrices) sans balayer le niveau de la feuille de l'index, et il peut trouver la plus grande date d'heure pendant la numérisation.

Aussi à 100 m + lignes, cette table peut être meilleure en tant que colonne en cluster au lieu d'un index en cluster Bree.

23