Quelle est l'importance de l'ordre des colonnes dans les index?


173

J'ai entendu dire que vous devriez mettre les colonnes qui seront les plus sélectives au début de la déclaration d'index. Exemple:

CREATE NONCLUSTERED INDEX MyINDX on Table1
(
   MostSelective,
   SecondMost,
   Least
)

Tout d'abord, ce que je dis est-il correct? Si tel est le cas, est-ce que je suis susceptible de voir de grandes différences de performances en réorganisant l'ordre des colonnes dans mon index ou est-ce plutôt une pratique «agréable à faire»?

La raison pour laquelle je pose la question est qu'après avoir mis une requête via le DTA, il m'a recommandé de créer un index contenant presque toutes les mêmes colonnes qu'un index existant, juste dans un ordre différent. J'envisageais simplement d'ajouter les colonnes manquantes à l'index existant et de l'appeler bien. Pensées?

Réponses:


193

Regardez un index comme celui-ci:

Cols
  1   2   3
-------------
|   | 1 |   |
| A |---|   |
|   | 2 |   |
|---|---|   |
|   |   |   |
|   | 1 | 9 |
| B |   |   |
|   |---|   |
|   | 2 |   |
|   |---|   |
|   | 3 |   |
|---|---|   |

Voyez comment restreindre sur A d'abord, car votre première colonne élimine plus de résultats que restreindre d'abord votre deuxième colonne? C'est plus facile si vous imaginez comment l'index doit être traversé, colonne 1, puis colonne 2, etc ... vous voyez que couper la plupart des résultats dans le premier passage rend la deuxième étape beaucoup plus rapide.

Un autre cas, si vous avez interrogé sur la colonne 3, l'optimiseur n'utiliserait même pas l'index, car il n'est pas du tout utile pour réduire les ensembles de résultats. Chaque fois que vous êtes dans une requête, réduire le nombre de résultats à traiter avant l'étape suivante signifie de meilleures performances.

Étant donné que l'index est également stocké de cette manière, il n'y a pas de retour arrière dans l'index pour trouver la première colonne lorsque vous l'interrogez.

En bref: non, ce n'est pas pour le spectacle, il y a de réels avantages en termes de performances.


13
Dans l'image ci-dessus, gardez à l'esprit que cet index ne serait bénéfique que si la colonne 1 était spécifiée dans la requête. Si votre requête ne spécifie que la colonne 2 dans le prédicat de jointure ou de recherche, cela ne serait pas avantageux. L'ordre compte donc là aussi. Peut-être que cela va sans dire, mais je voulais le mentionner.
CodeCowboyOrg

3
Gardez également à l'esprit, supposons que votre index est comme l'image ci-dessus et que votre requête filtre sur la colonne1 et la colonne2, mais que la colonne2 est plus unique et que ce sur quoi vous voulez vraiment filtrer est en fait la colonne2, alors il est plus avantageux d'avoir simplement un index où la colonne 2 est la première. Cela peut sembler contre-intuitif, mais gardez à l'esprit qu'un index est stocké sur plusieurs pages et est un arbre avec une plage de valeurs, tandis que la colonne 1 ci-dessus annule la moitié des possibilités, l'index sait déjà à quelle page d'index aller directement pour le Colonne2, il n'est pas nécessaire d'avoir besoin de la colonne 1 pour affiner l'ensemble.
CodeCowboyOrg

4
Cette image n'est pas une représentation précise de la façon dont les index sont structurés ou parcourus. Ont soumis une réponse rectifiant ce stackoverflow.com/a/39080819/73226
Martin Smith

6
@MartinSmith Je ne suis pas d'accord sur le fait que ce soit inexact. Il est certes extrêmement simplifié, ce qui était mon intention. Votre réponse, qui approfondit beaucoup plus les détails sur les niveaux, est cependant appréciée pour ceux qui veulent approfondir leurs connaissances. Si vous regardez l'image de votre arbre, vous verrez ce que j'illustre d'une manière très simple. Ce n'est pas très unique ni même spécifique à SQL; L'indexation des arbres B est assez courante dans tant de choses.
Nick Craver

@MartinSmith Je ne suis pas non plus d'accord sur le fait que ce soit inexact, ce que vous décrivez est le comportement standard de la manière d'arriver à couvrir l'index - la sélectivité est beaucoup plus importante une fois que vous effectuez des requêtes de plage car cela minimise le nombre de pages d'index que l'optimiseur doit scanner; cela peut être important dans les grandes tables avec des millions de lignes
Paul Hatcher

127

L'ordre des colonnes est critique. Maintenant, quel ordre est correct, cela dépend de la façon dont vous allez l'interroger. Un index peut être utilisé pour effectuer une recherche exacte ou un balayage de plage. Une recherche exacte se produit lorsque les valeurs de toutes les colonnes de l'index sont spécifiées et que la requête arrive exactement sur la ligne concernée. Pour les recherches, l'ordre des colonnes n'est pas pertinent. Une analyse de plage se produit lorsque seules certaines colonnes sont spécifiées et, dans ce cas, lorsque l'ordre devient important. SQL Server peut utiliser un index pour une analyse de plage uniquement si la colonne la plus à gauche est spécifiée, et uniquement si la colonne la plus à gauche suivante est spécifiée, et ainsi de suite. Si vous avez un index sur (A, B, C), il peut être utilisé pour rechercher A=@a, pour A=@a AND B=@bmais pas pour B=@b, pour C=@cni B=@b AND C=@c. Le cas A=@a AND C=@cest mixte, comme dans leA=@apartie utilisera l'index, mais C=@cpas (la requête analysera toutes les valeurs B pour A=@a, ne «sautera» pas C=@c). D'autres systèmes de base de données ont ce qu'on appelle l'opérateur «skip scan» qui peut tirer parti des colonnes internes d'un index lorsque les colonnes externes ne sont pas spécifiées.

Avec ces connaissances en main, vous pouvez revoir les définitions d'index. Un index sur (MostSelective, SecondMost, Least)ne sera effectif que lorsque la MostSelectivecolonne est spécifiée. Mais cela étant le plus sélectif, la pertinence des colonnes internes se dégradera rapidement. Très souvent, vous constaterez qu'un meilleur index est activé (MostSelective) include (SecondMost, Least)ou activé (MostSelective, SecondMost) include (Least). Parce que les colonnes internes sont moins pertinentes, placer des colonnes à faible sélectivité dans de telles positions correctes dans l'index ne les fait rien d'autre que du bruit pour une recherche, il est donc logique de les déplacer hors des pages intermédiaires et de les conserver uniquement sur les pages feuilles, pour à des fins de couverture des requêtes. En d'autres termes, déplacez-les vers INCLUDE. Cela devient plus important à mesure que la taille de la Leastcolonne augmente. L'idée est que cet index ne peut bénéficier qu'aux requêtes qui spécifientMostSelective soit comme valeur exacte, soit comme plage, et cette colonne étant la plus sélective, elle restreint déjà dans une large mesure les lignes candidates.

D'un autre côté, un index sur (Least, SecondMost, MostSelective)peut sembler une erreur, mais c'est en fait un indice assez puissant. Comme il a la Leastcolonne comme requête la plus externe, il peut être utilisé pour les requêtes qui doivent regrouper les résultats sur des colonnes à faible sélectivité. De telles requêtes sont répandues dans les entrepôts de données OLAP et d'analyse, et c'est exactement là que ces index ont de très bons arguments. De tels index constituent en fait d'excellents index clusterisés , précisément parce qu'ils organisent la disposition physique sur de grands morceaux de lignes associées (même Leastvaleur, qui indiquent généralement une sorte de catégorie ou de type) et ils facilitent les requêtes d'analyse.

Donc, malheureusement, il n'y a pas d'ordre «correct». Vous ne devez suivre aucune recette de coupe-biscuits, mais plutôt analyser le modèle de requête que vous allez utiliser sur ces tables et décider quel ordre de colonne d'index est le bon.


3
Super réponse comme d'habitude Remus. Je vais relire votre troisième paragraphe plusieurs fois et faire un suivi. Je soupçonne que c'est peut-être exactement ce que je dois faire.
Abe Miessler

"SQL Server peut utiliser un index pour une analyse de plage uniquement si la colonne la plus à gauche est spécifiée, et uniquement si la colonne la plus à gauche suivante est spécifiée, et ainsi de suite." C'est exactement ce qui manquait à ma compréhension, merci! Je ne savais pas que les balayages de plage ne pouvaient être effectués que sur la colonne d'index utilisée la plus à droite, mais maintenant que je le fais, cela a tellement de sens.
Allon Guralnek

Cette explication s'applique-t-elle à Oracle DB?
un autre

1
@Roizpi Oui, fondamentalement, toute base de données de relations avec des index fonctionne de la même manière ou d'une manière très similaire.
Tatranskymedved le

45

Comme le dit Remus, cela dépend de votre charge de travail.

Je veux cependant aborder un aspect trompeur de la réponse acceptée.

Pour les requêtes qui exécutent une recherche d'égalité sur toutes les colonnes de l'index, il n'y a pas de différence significative.

Le tableau ci-dessous crée deux tableaux et les remplit avec des données identiques. La seule différence est que l'un a les clés classées du plus au moins sélectif et l'autre l'inverse.

CREATE TABLE Table1(MostSelective char(800), SecondMost TINYINT, Least  CHAR(1), Filler CHAR(4000) null);
CREATE TABLE Table2(MostSelective char(800), SecondMost TINYINT, Least  CHAR(1), Filler CHAR(4000) null);

CREATE NONCLUSTERED INDEX MyINDX on Table1(MostSelective,SecondMost,Least);
CREATE NONCLUSTERED INDEX MyINDX2 on Table2(Least,SecondMost,MostSelective);

INSERT INTO Table1 (MostSelective, SecondMost, Least)
output inserted.* into Table2
SELECT TOP 26 REPLICATE(CHAR(number + 65),800), number/5, '~'
FROM master..spt_values
WHERE type = 'P' AND number >= 0
ORDER BY number;

Maintenant, faites une requête sur les deux tables ...

SELECT *
FROM   Table1
WHERE  MostSelective = REPLICATE('P', 800)
       AND SecondMost = 3
       AND Least = '~';

SELECT *
FROM   Table2
WHERE  MostSelective = REPLICATE('P', 800)
       AND SecondMost = 3
       AND Least = '~'; 

... Les deux utilisent une amende indexée et les deux reçoivent exactement le même coût.

entrez la description de l'image ici

L'art ASCII dans la réponse acceptée n'est en fait pas la façon dont les index sont structurés. Les pages d'index de Table1 sont représentées ci-dessous (cliquez sur l'image pour l'ouvrir en taille réelle).

entrez la description de l'image ici

Les pages d'index contiennent des lignes contenant la clé entière (dans ce cas, il y a en fait une colonne clé supplémentaire ajoutée pour l'identificateur de ligne car l'index n'a pas été déclaré comme unique mais qui peut être ignoré, des informations supplémentaires à ce sujet peuvent être trouvées ici ).

Pour la requête ci-dessus, SQL Server ne se soucie pas de la sélectivité des colonnes. Il effectue une recherche binaire de la page racine et découvre que la clé (PPP...,3,~ ) est >=(JJJ...,1,~ )et < (SSS...,3,~ )doit donc lire la page 1:118. Il effectue ensuite une recherche binaire des entrées clés de cette page et localise la page feuille vers laquelle se déplacer.

La modification de l'index par ordre de sélectivité n'affecte ni le nombre attendu de comparaisons clés de la recherche binaire ni le nombre de pages à parcourir pour effectuer une recherche d'index. Au mieux, cela pourrait légèrement accélérer la comparaison clé elle-même.

Parfois, la commande de l'index le plus sélectif en premier aura du sens pour d'autres requêtes de votre charge de travail.

Par exemple, si la charge de travail contient des requêtes des deux formes suivantes.

SELECT * ... WHERE  MostSelective = 'P'

SELECT * ...WHERE Least = '~'

Les index ci-dessus ne couvrent ni l'un ni l'autre. MostSelectiveest suffisamment sélectif pour faire un plan avec une recherche et des recherches utiles, mais la requête contre Leastne l'est pas.

Cependant, ce scénario (recherche d'index non couvrant sur un sous-ensemble de colonne (s) de tête d'un index composite) n'est qu'une classe de requête possible qui peut être aidée par un index. Si vous ne recherchez jamais par MostSelectivelui-même ou par une combinaison de MostSelective, SecondMostet toujours par une combinaison des trois colonnes, cet avantage théorique vous est inutile.

À l'inverse, des requêtes telles que

SELECT MostSelective,
       SecondMost,
       Least
FROM   Table2
WHERE  Least = '~'
ORDER  BY SecondMost,
          MostSelective 

Serait aidé en ayant l'ordre inverse de celui couramment prescrit - car il couvre la requête, peut prendre en charge une recherche et retourne les lignes dans l'ordre souhaité pour démarrer.

Il s'agit donc d'un conseil souvent répété, mais il s'agit tout au plus d'une heuristique sur les avantages potentiels d' autres requêtes - et cela ne remplace pas le fait d'examiner votre charge de travail.


31

vous devez mettre les colonnes qui seront les plus sélectives au début de la déclaration d'index.

Correct. Les index peuvent être composites - composés de plusieurs colonnes - et l'ordre est important en raison du principe le plus à gauche. La raison en est que la base de données vérifie la liste de gauche à droite et doit trouver une référence de colonne correspondante correspondant à l'ordre défini. Par exemple, avoir un index sur une table d'adresses avec des colonnes:

  • Adresse
  • Ville
  • Etat

Toute requête utilisant la addresscolonne peut utiliser l'index, mais si la requête n'a que des références cityet / ou state, l'index ne peut pas être utilisé. C'est parce que la colonne la plus à gauche n'est pas référencée. Les performances des requêtes doivent vous indiquer ce qui est optimal: des index individuels ou plusieurs composites avec des ordres différents. Bonne lecture: The Tipping Point , par Kimberley Tripp


Et si c'était seulement la colonne la plus à droite qui n'était pas utilisée? Donc, une requête a utilisé l'adresse et la ville, mais PAS l'état. L'index serait-il alors utilisé?
Abe Miessler

@Abe: Le plus à droite ne serait pas utilisé - vous devez satisfaire l'ordre d'index en commençant par la gauche. Manquez-en un, je ne peux pas l'utiliser.
OMG Ponies

4
@Abe: Si vous avez posé une question sur l'adresse et la ville, mais PAS sur l'état - alors oui, l'index serait utilisé. En d'autres termes, la base de données est capable d'utiliser des index partiels pour satisfaire une demande, à condition qu'elle puisse commencer à partir de la gauche d'un index et se déplacer vers la droite en utilisant les champs qui sont interrogés. Si, cependant, vous avez interrogé en utilisant l'adresse et l'état, mais PAS la ville, il peut toujours utiliser l'index, mais ce ne sera pas aussi efficace - car maintenant il ne peut utiliser que la partie adresse de l'index (b / c est ensuite city ​​et il n'est pas utilisé dans la requête).
JaredC

6

Toutes les autres réponses sont fausses.

La sélectivité des colonnes individuelles dans un index composite n'a pas d' importance lors du prélèvement de la commande.

Voici le processus de réflexion simple: En effet, un index est la concaténation des colonnes impliquées.

En donnant cette justification, la seule différence est de comparer deux «chaînes» qui diffèrent plus tôt que plus tard dans la chaîne. C'est une infime partie du coût total. Il n'y a pas de «premier passage / deuxième passage», comme mentionné dans une réponse.

Alors, quel ordre doit être utilisé?

  1. Commencez par la ou les colonnes testées avec =, dans n'importe quel ordre.
  2. Puis clouez sur une colonne de distance.

Par exemple, la colonne de très faible sélectivité doit venir en premier dans ceci:

WHERE deleted = 0  AND  the_datetime > NOW() - INTERVAL 7 DAY
INDEX(deleted, the_datetime)

Changer l'ordre dans l'index le ferait totalement ignorer deleted.

(Il y a beaucoup plus de règles pour classer les colonnes.)


Le vote négatif est-il parce que je me trompe? Ou parce que j'ai une forte opinion? Ou autre chose?
Rick James

n'était pas mon vote défavorable, mais supprimé = 0 pour moi, ce n'est pas une faible sélectivité? J'imagine que ce serait la majorité des lignes du tableau.
Greg

@Greg - Je pense que cela signifie "faible sélectivité" - Autrement dit, l'utilisation deletedn'aide pas beaucoup à filtrer les lignes indésirables. Avez-vous un meilleur exemple? (C'est celui qui m'est venu à l'esprit lorsque j'ai écrit la réponse.)
Rick James

Malentendu de ma part.
Greg

1
@ClickOk - Merci. Mon livre de cuisine donne quelques informations de base: mysql.rjweb.org/doc.php/index_cookbook_mysql
Rick James
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.