Ordre de stockage vs Ordre des résultats


8

Il s'agit d'une question dérivée de l' ordre de tri spécifié dans la clé primaire, mais le tri est exécuté sur SELECT .

@Catcall le dit au sujet de l'ordre de stockage (index clusterisé) et de l'ordre de sortie

Beaucoup de gens pensent qu'un index clusterisé garantit un ordre de tri sur la sortie. Mais ce n'est pas ce qu'il fait; il garantit un ordre de stockage sur disque. Voir, par exemple, cet article de blog .

J'ai lu l'article de blog de Hugo Kornelis et je comprends qu'un index ne garantit pas que le serveur SQL lit les enregistrements dans un ordre spécifique. Pourtant, j'ai du mal à accepter que je ne peux pas assumer cela pour mon scénario?

CREATE TABLE [dbo].[SensorValues](
  [DeviceId] [int] NOT NULL,
  [SensorId] [int] NOT NULL,
  [SensorValue] [int] NOT NULL,
  [Date] [int] NOT NULL,
CONSTRAINT [PK_SensorValues] PRIMARY KEY CLUSTERED 
(
  [DeviceId] ASC,
  [SensorId] ASC,
  [Date] DESC
) WITH (
    FILLFACTOR=75,
    DATA_COMPRESSION = PAGE,
    PAD_INDEX = OFF,
    STATISTICS_NORECOMPUTE = OFF,
    SORT_IN_TEMPDB = OFF,
    IGNORE_DUP_KEY = OFF,
    ONLINE = OFF,
    ALLOW_ROW_LOCKS = ON,
    ALLOW_PAGE_LOCKS = ON)
  ON [MyPartitioningScheme]([Date])

Ma requête d'origine était la suivante:

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010
  ORDER BY Date DESC

Mais je suggère que je pourrais aussi bien utiliser celui-ci (lire ci-dessous pour mon explication):

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010

Comme vous pouvez le voir, mes lignes de table sont petites (16 octets) et je n'ai qu'un seul index, un cluster. Dans mon scénario, la table se compose de 100 000 000 d'enregistrements en ce moment (et cela augmentera très probablement dix fois).

Lorsque le serveur de base de données interroge cette table, il a deux façons de trouver mes lignes, soit il recherche la clé primaire et donc lit et renvoie mes valeurs en desc. ordre de date, ou il doit faire une analyse complète de la table. Ma conclusion est qu'une analyse complète de la table sur tous ces enregistrements sera beaucoup trop lente et le serveur de base de données cherchera donc toujours la table via sa clé primaire et retournera ainsi les valeurs triées parDate DESC


2
Pourquoi voulez-vous pouvoir vous appuyer si mal sur cette hypothèse? Pourquoi ne mettez-vous pas simplement ORDER BYdessus, alors vous savez que vous pouvez vous y fier. Voir # 3 ici
Aaron Bertrand

Pour 2 raisons, par curiosité et parce que la ORDER BYclause est un gros coup de performance pour moi (lire l' autre question pour plus d'infos). J'ai une solution qui fonctionne pour l'instant, mais elle ne tiendra pas quand et si mon trafic augmente.
m__

1
ORDER BY ne devrait pas être un succès si vous comptez sur l'ordre que vous voyez sans l'ordre par - cela n'a pas de sens pour moi.
Aaron Bertrand

4
La seule chose qui garantit l' ordre des résultats est une ORDER BYclause dans votre requête. Cela est vrai pour SQL Server , Oracle , MySQL et tout autre SGBDR auquel vous pouvez penser. Essayez autre chose et vous vous préparez pour une tasse surprise de FAIL.
Nick Chammas

Réponses:


15

Permettez-moi d'essayer d'expliquer pourquoi vous ne devriez pas faire cela, pourquoi vous ne devriez jamais supposer qu'un produit SQL retournera un jeu de résultats dans un ordre spécifique, sauf si vous le spécifiez, quels que soient les indices - cluster ou non cluster, B-trees ou R-Trees ou kd-trees ou fractal-trees ou tout autre indice exotique utilisé par un SGBD.


Votre requête d'origine indique au SGBD de rechercher dans le SensorValuestableau, de trouver des lignes qui correspondent aux 3 conditions, de classer ces lignes par ordre Datedécroissant, de ne conserver que la première ligne de celles-ci et - enfin - de sélectionner et de renvoyer uniquement la SensorValuecolonne.

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010
  ORDER BY Date DESC ;

Ce sont des ordres très spécifiques que vous avez donnés au SGBD et le résultat sera probablement le même à chaque fois que vous exécutez la requête (il y a une chance que ce ne soit pas le cas, si vous avez plus d'une ligne qui correspond aux conditions et qui ont les mêmes max Datemais différent, SensorValuemais supposons pour le reste de la conversation qu'aucune ligne de ce type n'existe dans votre table).

Le SGBD doit-il le faire, pour exécuter cette requête, de la façon exacte dont je le décris ci-dessus? Non, bien sûr que non et vous le savez. Il peut ne pas lire la table mais lire à partir d'un index. Ou il peut utiliser deux index s'il pense que c'est mieux (plus vite). Ou trois. Ou il peut utiliser un résultat mis en cache (pas SQL Server mais d'autres résultats de requête de cache de SGBD). Ou il peut utiliser une exécution parallèle une fois et non la prochaine fois qu'il s'exécute. Ou ... (ajoutez toute autre fonctionnalité qui affecte l'exécution et les plans d'exécution).

Ce qui est garanti cependant, c'est qu'il retournera exactement le même résultat, chaque fois que vous l'exécuterez - tant qu'aucune ligne n'est insérée, supprimée ou mise à jour.


Voyons maintenant ce que dit votre suggestion:

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010 ;

Cette requête indique au SGBD de rechercher la SensorValuestable, de trouver des lignes qui correspondent aux 3 conditions, de classer ces lignes par ordre Datedécroissant , de ne pas se soucier de l'ordre, de ne conserver qu'une seule ligne et - enfin - de sélectionner et de renvoyer uniquement la SensorValuecolonne.

Donc, il dit essentiellement la même chose que la première, sauf qu'il dit que vous ne voulez qu'un seul résultat qui correspond aux conditions et que vous ne vous souciez pas lequel .

Maintenant, pouvons-nous supposer qu'il donnera toujours le même résultat à cause de l'index clusterisé?
- S'il utilise cet index clusterisé à chaque fois, oui.

Mais l'utilisera-t-il?
- Non.

Pourquoi pas?
- Beacuse ça peut. L'optimiseur de requêtes est libre de choisir un chemin d'exécution à chaque fois qu'il exécute une instruction. Quelle que soit la voie qu'il juge appropriée à ce moment-là pour cette déclaration.

Mais l'utilisation de l'index cluster n'est-elle pas la manière la meilleure / la plus rapide d'obtenir des résultats?
- Non, pas toujours. Ce peut être la première fois que vous exécutez la requête. La deuxième fois, il peut utiliser un résultat mis en cache (si le SGBD possède une telle fonctionnalité, pas SQL Server * ). La 1000e fois, le résultat peut avoir été supprimé du cache et un autre résultat peut y exister. Disons que vous avez exécuté cette requête juste avant:

SELECT TOP 1 SensorValue
  FROM SensorValues
  WHERE SensorId = 53
    AND DeviceId = 3819
    AND Date < 1339225010
  ORDER BY Date ASC ;         --- Notice the `ASC` here

et le résultat mis en cache (à partir de la requête ci-dessus) en est un autre, différent, qui correspond toujours à vos conditions mais n'est pas le premier dans votre commande (souhaitée). Et vous avez dit au SGBD de ne pas se soucier de la commande.

OK, donc seul le cache peut affecter cela?
- Non, beaucoup d'autres choses aussi.

  • d'autres index ont été considérés, à l'époque par le SGBD, comme meilleurs pour cette requête.
  • un développeur a modifié ou supprimé complètement cet index cluster que vous aviez.
  • vous ou un autre développeur avez ajouté un autre index que l'optimiseur a décidé qu'il était plus efficace à utiliser que le CI.
  • vous avez mis à jour vers une nouvelle version et le nouvel optimiseur a un bug mineur ou un changement dans la façon dont il se classe et choisit les plans d'exécution.
  • les statistiques ont été mises à jour.
  • l'exécution parallèle a été choisie à la place.

*: SQL Server ne met pas en cache les résultats des requêtes, mais l'édition Enterprise possède une fonctionnalité d' analyse avancée qui est un peu similaire dans la mesure où vous pouvez obtenir des résultats différents en raison de requêtes simultanées. Je ne sais pas exactement quand cela se produira. (Thnx @Martin Smith pour l'astuce.)


J'espère que vous êtes convaincu que vous ne devez jamais compter qu'une requête SQL retournera les résultats dans un ordre spécifique, sauf si vous le spécifiez. Et n'utilisez jamais TOP (n)sans ORDER BY, à moins bien sûr que vous vouliez juste n lignes dans le résultat et que vous ne vous souciez pas de celles qui sont retournées.


2
SQL Server Enterprise Edition possède une fonctionnalité d' analyse avancée qui est un peu similaire dans la mesure où vous pouvez obtenir des résultats différents en raison de requêtes simultanées. Je ne sais pas exactement quand cela se produira.
Martin Smith

1
Une autre chose qui "randomise" potentiellement l'ordre des résultats (même si la requête est apparemment pilotée par un index ordonné) est le parallélisme. J'ai vu une application qui avait heureusement exécuté un SQL cassé commencer à se comporter mal après avoir activé le parallélisme automatique (pas SQL Server, mais je suppose que cela pourrait s'appliquer là aussi).
mat
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.