web-dev-qa-db-fra.com

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

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, suis-je susceptible de voir de grandes différences de performances en réarrangeant l'ordre des colonnes dans mon index ou s'agit-il davantage d'une pratique "agréable à faire"?

La raison pour laquelle je pose la question est parce que, après avoir interrogé le DTA, il m'a été recommandé de créer un index contenant presque toutes les mêmes colonnes qu'un index existant, mais dans un ordre différent. J'envisageais simplement d'ajouter les colonnes manquantes à l'index existant et de le qualifier de bon. Pensées?

149
Abe Miessler

Regardez un index comme celui-ci:

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

Vous voyez comment restreindre le premier, car votre première colonne élimine plus de résultats que ne restreint votre deuxième colonne en premier? C'est plus facile si vous voyez comment l'index doit être parcouru dans la colonne 1, puis dans la colonne 2, etc. Vous constatez que supprimer la plupart des résultats lors de la première passe accélère d'autant plus la 2e étape.

Dans un autre cas, si vous interrogez sur la colonne 3, l'optimiseur n'utiliserait même pas l'index, car il n'est pas du tout utile de 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 la prochaine étape signifie de meilleures performances.

Comme l'index est également stocké de cette façon, il n'y a pas de retour en arrière sur l'index pour rechercher la première colonne lorsque vous interrogez dessus.

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

178
Nick Craver

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 distance. Une recherche exacte correspond au moment où les valeurs de toutes les colonnes de l'index sont spécifiées et que la requête atterrit exactement sur la ligne. L'intérêt de recherche est que l'ordre des colonnes ne soit pas pertinent. Un balayage 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 un balayage 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, etc. Si vous avez un index sur (A, B, C), il peut être utilisé pour effectuer un balayage de plage pour A=@a, pour A=@a AND B=@b mais pas pour B=@b, pour C=@c norB=@b AND C=@c. L'affaire A=@a AND C=@c est mixte, comme dans le A=@a La partie utilisera l'index, mais le C=@c non (la requête analysera toutes les valeurs B pour A=@a, ne passera 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 cette connaissance en main, vous pouvez revoir les définitions d'index. Un index sur (MostSelective, SecondMost, Least) ne sera efficace que lorsque la colonne MostSelective sera spécifiée. Mais ceci étant le plus sélectif, la pertinence des colonnes intérieures se dégradera rapidement. Très souvent, vous constaterez qu'un meilleur index est sur (MostSelective) include (SecondMost, Least) ou sur (MostSelective, SecondMost) include (Least). Comme les colonnes intérieures sont moins pertinentes, le fait de placer les colonnes à faible sélectivité à des positions aussi justes dans l’index ne les rend que bruyantes pour une recherche; il est donc logique de les déplacer en dehors des pages intermédiaires et de ne les conserver que sur les feuilles. interroger les objectifs de couvrabilité. En d'autres termes, déplacez-les sur INCLUDE. Cela devient d'autant plus important que la taille de la colonne Least augmente. L'idée est que cet index ne peut que bénéficier aux requêtes spécifiant MostSelective en tant que valeur exacte ou plage, et cette colonne étant la plus sélective, elle restreint déjà dans une large mesure les lignes candidates.

D'autre part un index sur (Least, SecondMost, MostSelective) peut sembler une erreur, mais il s’agit en fait d’un index assez puissant. Comme la requête la plus externe est la colonne Least, elle peut être utilisée pour les requêtes qui doivent agréger les résultats sur des colonnes à faible sélectivité. De telles requêtes prévalent dans OLAP et les entrepôts de données d’analyse, et c’est précisément là que de tels index ont un très bon exemple. Ils sont en fait excellents les index groupés , précisément parce qu'ils organisent la présentation physique sur de gros morceaux de lignes associées (même valeur Least, qui indiquent généralement une sorte de catégorie ou de type) et facilitent les requêtes d'analyse .

Donc, malheureusement, il n'y a pas d'ordre "correct". Vous ne devez pas suivre une recette de découpe de cookie, mais plutôt analyser le motif de requête que vous allez utiliser par rapport à ces tables et décider quel ordre de colonne d'index est correct.

114
Remus Rusanu

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 effectuent 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 tables et les remplit avec des données identiques. La seule différence est que l’une a les touches ordonné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, faire 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 reçoivent exactement le même coût.

enter image description here

Dans la réponse acceptée, l’art ASCII) n’est pas en fait la structure des index. Les pages d’index de Table1 sont représentées ci-dessous (cliquez sur l’image pour l’ouvrir en taille réelle).

enter image description here

Les pages d'index contiennent des lignes contenant la clé entière (dans ce cas, une colonne de clé supplémentaire est ajoutée pour l'identificateur de ligne, car l'index n'a pas été déclaré unique mais peut être ignoré pour plus d'informations à ce sujet, cliquez 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 touche (PPP...,3,~ ) Est >=(JJJ...,1,~ ) et < (SSS...,3,~ ) il faut donc lire la page 1:118. Il effectue ensuite une recherche binaire des entrées de clé sur cette page et localise la page feuille vers laquelle descendre.

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

Cependant, il est parfois utile de commander d'abord l'index le plus sélectif pour d'autres requêtes de votre charge de travail.

E.g 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. MostSelective est suffisamment sélectif pour établir un plan avec une recherche et des recherches intéressantes, mais la requête contre Least ne l’est pas.

Toutefois, ce scénario (recherche d’index non couvrante sur un sous-ensemble de colonne (s) de tête d’un index composite) n’est qu’une des classes possibles de requêtes pouvant être facilitées par un index. Si vous n'effectuez jamais une recherche par MostSelective seule ou par une combinaison de MostSelective, SecondMost Et recherchez toujours par une combinaison des trois colonnes, cet avantage théorique ne vous sera d'aucune utilité.

Inversement, 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 renvoie les lignes dans l'ordre souhaité pour démarrer.

C'est donc un conseil souvent répété, mais tout au plus une heuristique sur le bénéfice potentiel des autres requêtes - et il ne peut en aucun cas remplacer le fait de regarder votre charge de travail .

41
Martin Smith

vous devriez 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 colonne address peut utiliser l'index, mais si la requête ne comporte que city et/ou state références, l'index ne peut pas être utilisé. En effet, 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 des composites multiples avec des ordres différents. Bonne lecture: The Tipping Point , de Kimberley Tripp

30
OMG Ponies

Toutes les réponses sont fausses.

La sélectivité des colonnes individuelles dans un index composite n'a pas d'importance lors de la sélection de la commande.

Voici le processus de pensée simple: Effectivement, un index est la concaténation des colonnes impliquées.

Compte tenu de ce qui précède, la seule différence consiste à comparer deux "chaînes" différentes plus tôt que plus tard dans la chaîne. Ceci ne représente qu'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 les colonnes testées avec =, dans n'importe quel ordre .
  2. Puis fixez une colonne de gamme.

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

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

Si vous inversez la commande dans l’index, il ignorerait totalement deleted.

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

3
Rick James