Pourquoi utiliser la clause INCLUDE lors de la création d'un index?

432

En étudiant pour l'examen 70-433, j'ai remarqué que vous pouvez créer un indice de couverture de l'une des deux manières suivantes.

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

-- OU --

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

La clause INCLUDE est nouvelle pour moi. Pourquoi l'utiliseriez-vous et quelles directives suggéreriez-vous pour déterminer s'il faut créer un index de couverture avec ou sans la clause INCLUDE?

Cory
la source

Réponses:

364

Si la colonne n'est pas dans le WHERE/JOIN/GROUP BY/ORDER BY, mais seulement dans la liste des colonnes de la SELECTclause.

La INCLUDEclause ajoute les données au niveau le plus bas / feuille, plutôt que dans l'arborescence d'index. Cela rend l'index plus petit car il ne fait pas partie de l'arbre

INCLUDE columnsne sont pas des colonnes clés dans l'index, elles ne sont donc pas ordonnées. Cela signifie qu'il n'est pas vraiment utile pour les prédicats, le tri, etc. comme je l'ai mentionné ci-dessus. Cependant, cela peut être utile si vous avez une recherche résiduelle dans quelques lignes de la ou des colonnes clés

Un autre article MSDN avec un exemple concret

gbn
la source
7
Alors, ce serait une technique pour créer une version moins chère d'un index couvert?
JMarsch
3
@gbn, pourriez-vous expliquer cette phrase plus en détail et expliquer pourquoi cela signifie que la clause include n'est pas utile pour le tri, etc.: "La clause INCLUDE ajoute les données au niveau le plus bas / feuille, plutôt que dans l'arborescence d'index . Cela rend l'indice plus petit car il ne fait pas partie de l'arbre "
Tola Odejayi
4
@JMarsch: désolé pour la réponse tardive, mais oui, c'est exactement ce que c'est.
gbn
10
@Tola Odejayi: les colonnes INCLUDE ne sont pas des colonnes clés dans l'index, elles ne sont donc pas ordonnées. Cela les rend généralement inutiles pour les JOIN ou le tri. Et comme ce ne sont pas des colonnes clés, elles ne se trouvent pas dans toute la structure de l'arborescence B comme les colonnes clés
gbn
4
Bien que ce soit la réponse la plus acceptée, je pense que des explications supplémentaires sont nécessaires, que se passe-t-il si pour certaines requêtes la colonne fait partie de la SELECTet pour d'autres non? \
Chisko
215

Vous utiliseriez INCLUDE pour ajouter une ou plusieurs colonnes au niveau feuille d'un index non clusterisé, si ce faisant, vous pouvez "couvrir" vos requêtes.

Imaginez que vous devez rechercher l'ID d'un employé, l'ID du service et le nom de famille.

SELECT EmployeeID, DepartmentID, LastName
FROM Employee
WHERE DepartmentID = 5

S'il vous arrive d'avoir un index non clusterisé sur (EmployeeID, DepartmentID), une fois que vous avez trouvé les employés pour un département donné, vous devez maintenant faire une "recherche de signet" pour obtenir le véritable enregistrement complet de l'employé, juste pour obtenir la colonne du nom de famille . Cela peut devenir assez cher en termes de performances, si vous trouvez beaucoup d'employés.

Si vous aviez inclus ce nom de famille dans votre index:

CREATE NONCLUSTERED INDEX NC_EmpDep 
  ON Employee(EmployeeID, DepartmentID)
  INCLUDE (Lastname)

toutes les informations dont vous avez besoin sont alors disponibles au niveau feuille de l'index non clusterisé. Juste en cherchant dans l'index non clusterisé et en trouvant vos employés pour un département donné, vous avez toutes les informations nécessaires, et la recherche de signets pour chaque employé trouvé dans l'index n'est plus nécessaire -> vous gagnez beaucoup de temps.

De toute évidence, vous ne pouvez pas inclure chaque colonne dans chaque index non clusterisé - mais si vous avez des requêtes auxquelles il manque une ou deux colonnes à "couvrir" (et qui sont souvent utilisées), il peut être très utile d'INCLURE celles-ci. dans un index non cluster approprié.

marc_s
la source
25
Voulez-vous vraiment utiliser cet index? Pourquoi EmployeeID? Vous avez seulement besoin de DepartmentID dans les colonnes clés? Vous avez été cité ici comme faisant autorité: stackoverflow.com/q/6187904/27535
gbn
3
Votre explication est bonne mais ne correspond pas vraiment au cas d'utilisation que vous décrivez. La ou les colonnes clés doivent se trouver sur le ou les filtres JOINde la requête, et les INCLUDEs doivent être les données que vous récupérez mais pas le tri.
JNK
15
Tout d'abord, l'index Employee (EmployeeID, DepartmentID) ne sera pas utilisé pour filtrer DepartmentID = 5. Parce que sa commande ne correspond pas
AnandPhadke
29

Cette discussion passe à côté du point important: la question n'est pas de savoir s'il est préférable d'inclure les "colonnes non clés" comme colonnes d' index ou comme colonnes incluses .

La question est de savoir combien il est coûteux d'utiliser le mécanisme d'inclusion pour inclure des colonnes qui ne sont pas vraiment nécessaires dans l'index ? (ne fait généralement pas partie des clauses where, mais est souvent inclus dans les sélections). Votre dilemme est donc toujours:

  1. Utilisez l'index sur id1, id2 ... idN seul ou
  2. Utiliser l'index sur id1, id2 ... idN plus inclure col1, col2 ... colN

Où: id1, id2 ... idN sont des colonnes souvent utilisées dans les restrictions et col1, col2 ... colN sont des colonnes souvent sélectionnées, mais généralement pas utilisées dans les restrictions

(L'option d'inclure toutes ces colonnes dans le cadre de la clé d'index est juste toujours idiote (à moins qu'elles ne soient également utilisées dans des restrictions) - car il serait toujours plus coûteux à maintenir car l'index doit être mis à jour et trié même lorsque le "clés" n'ont pas changé).

Utilisez donc l'option 1 ou 2?

Réponse: Si votre table est rarement mise à jour - principalement insérée dans / supprimée de - alors il est relativement peu coûteux d'utiliser le mécanisme d'inclusion pour inclure certaines "colonnes chaudes" (qui sont souvent utilisées dans les sélections - mais pas souvent dans les restrictions) car les insertions / suppressions nécessitent de toute façon la mise à jour / tri de l'index et donc peu de surcharge supplémentaire est associée au stockage de quelques colonnes supplémentaires tout en mettant déjà à jour l'index. La surcharge correspond à la mémoire supplémentaire et au processeur utilisés pour stocker des informations redondantes sur l'index.

Si les colonnes que vous envisagez d'ajouter en tant que colonnes incluses sont souvent mises à jour (sans que la clé d' index -colonnes soit mise à jour) - ou - si elles sont si nombreuses que l'index se rapproche d'une copie de votre table - utilisez l'option 1 Je suggère! De plus, si l'ajout de certaines colonnes d'inclusion ne fait aucune différence dans les performances - vous voudrez peut-être ignorer l'idée de les ajouter :) Vérifiez qu'elles sont utiles!

Le nombre moyen de lignes pour les mêmes valeurs dans les clés (id1, id2 ... idN) peut également avoir une certaine importance.

Notez que si une colonne - qui est ajoutée en tant que colonne d'index incluse - est utilisée dans la restriction : tant que l'index en tant que tel peut être utilisé (en fonction de la restriction par rapport à la clé d' index -colonnes) - SQL Server correspond la restriction de colonne par rapport à l'index (leaf-node-values) au lieu de faire le tour coûteux de la table elle-même.

Fredrik Solhaug
la source
18

Les colonnes d'index de base sont triées, mais les colonnes incluses ne sont pas triées. Cela économise des ressources dans la gestion de l'index, tout en permettant de fournir les données dans les colonnes incluses pour couvrir une requête. Ainsi, si vous souhaitez couvrir des requêtes, vous pouvez placer les critères de recherche pour localiser les lignes dans les colonnes triées de l'index, mais ensuite "inclure" des colonnes supplémentaires non triées avec des données non liées à la recherche. Cela aide certainement à réduire la quantité de tri et de fragmentation dans la maintenance des index.

onupdatecascade
la source
7

Les raisons pour lesquelles (y compris les données au niveau des feuilles de l'indice) ont été bien expliquées. La raison pour laquelle vous donnez deux secousses à ce sujet, c'est que lorsque vous exécutez votre requête, si vous n'avez pas les colonnes supplémentaires incluses (nouvelle fonctionnalité dans SQL 2005), SQL Server doit aller à l'index cluster pour obtenir les colonnes supplémentaires ce qui prend plus de temps et ajoute plus de charge au service SQL Server, aux disques et à la mémoire (le cache de tampon pour être spécifique) lorsque de nouvelles pages de données sont chargées en mémoire, ce qui peut potentiellement pousser d'autres données plus souvent nécessaires hors du cache de tampon.

mrdenny
la source
existe-t-il un moyen de prouver qu'il utilise en fait moins de mémoire? c'est ce à quoi je m'attendais aussi, mais je reçois un peu de statique à ce sujet au travail
Asken
Étant donné que vous devez charger la page du tas ou de l'index cluster en mémoire ainsi que la page d'index, ce qui signifie que vous mettez des données en double en mémoire, les calculs deviennent assez simples. Quant à un moyen de le mesurer spécifiquement, non, il n'y en a pas.
mrdenny
5

Une considération supplémentaire que je n'ai pas vue dans les réponses déjà données, est que les colonnes incluses peuvent être de types de données qui ne sont pas autorisés en tant que colonnes de clé d'index, telles que varchar (max).

Cela vous permet d'inclure de telles colonnes dans un index de couverture. J'ai récemment dû le faire pour fournir une requête générée par nHibernate, qui avait beaucoup de colonnes dans SELECT, avec un index utile.

Robin Hames
la source
3

Une des raisons de préférer INCLUDEles colonnes-clés si vous n'avez pas besoin de cette colonne dans la clé est la documentation. Cela rend l'évolution des index beaucoup plus facile à l'avenir.

Considérant votre exemple:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

Cet index est meilleur si votre requête ressemble à ceci:

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...

Bien sûr, vous ne devez pas mettre de colonnes INCLUDEsi vous pouvez obtenir un avantage supplémentaire de les avoir dans la partie clé. Les deux requêtes suivantes préfèrent en fait la col2colonne dans la clé de l'index.

SELECT col2, col3
  FROM MyTable
 WHERE col1 = ...
   AND col2 = ...
SELECT TOP 1 col2, col3
  FROM MyTable
 WHERE col1 = ...
 ORDER BY col2

Supposons que ce n'est pas le cas et que nous l'avons col2dans la INCLUDEclause car il n'y a tout simplement aucun avantage à l'avoir dans la partie arborescente de l'index.

Avance rapide de quelques années.

Vous devez régler cette requête:

SELECT TOP 1 col2
  FROM MyTable
 WHERE col1 = ...
 ORDER BY another_col

Pour optimiser cette requête, l'index suivant serait parfait:

CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2)

Si vous vérifiez déjà quels index vous avez sur cette table, votre index précédent pourrait toujours être là:

CREATE INDEX idx1 ON MyTable (Col1) INCLUDE (Col2, Col3)

Maintenant , vous savez que Col2et Col3ne faites pas partie de l'arbre d'index et ne sont donc pas utilisé pour réduire la plage d'index de lecture , ni pour commander les lignes. Il est plutôt sûr d'ajouter another_columnà la fin de la partie clé de l'index (après col1). Il y a peu de risques de casser quoi que ce soit:

DROP INDEX idx1 ON MyTable;
CREATE INDEX idx1 ON MyTable (Col1, another_col) INCLUDE (Col2, Col3);

Cet indice deviendra plus grand, ce qui comporte encore certains risques, mais il est généralement préférable d'étendre les indices existants plutôt que d'en introduire de nouveaux.

Si vous aviez un index sans INCLUDE, vous ne pourriez pas savoir quelles requêtes vous briseriez en ajoutant another_coljuste après Col1.

CREATE INDEX idx1 ON MyTable (Col1, Col2, Col3)

Que se passe-t-il si vous ajoutez another_colentre Col1et Col2? D'autres requêtes en souffriront-elles?

Il existe d'autres "avantages" par INCLUDErapport aux colonnes clés si vous ajoutez ces colonnes juste pour éviter de les extraire du tableau . Cependant, je considère l'aspect documentation comme le plus important.

Pour répondre à ta question:

Quelles lignes directrices suggéreriez-vous pour déterminer s'il faut créer un indice de couverture avec ou sans la clause INCLUDE?

Si vous ajoutez une colonne à l'index dans le seul but d'avoir cette colonne disponible dans l'index sans visiter la table, placez-la dans la INCLUDEclause.

Si l'ajout de la colonne à la clé d'index apporte des avantages supplémentaires (par exemple pour order byou parce qu'elle peut réduire la plage d'index de lecture), ajoutez-la à la clé.

Vous pouvez lire une discussion plus longue à ce sujet ici:

https://use-the-index-luke.com/blog/2019-04/include-columns-in-btree-indexes

Markus Winand
la source
2

Il existe une limite à la taille totale de toutes les colonnes intégrées dans la définition d'index. Cela dit, je n'ai jamais eu à créer un index aussi large. Pour moi, le plus grand avantage est le fait que vous pouvez couvrir plus de requêtes avec un index qui a inclus des colonnes car elles n'ont pas besoin d'être définies dans un ordre particulier. Pensez à est comme un index dans l'index. Un exemple serait le StoreID (où StoreID est une faible sélectivité, ce qui signifie que chaque magasin est associé à un grand nombre de clients), puis les données démographiques des clients (LastName, FirstName, DOB): si vous insérez simplement ces colonnes dans cet ordre (StoreID, LastName , FirstName, DOB), vous ne pouvez rechercher efficacement que les clients pour lesquels vous connaissez StoreID et LastName.

D'un autre côté, définir l'index sur StoreID et inclure les colonnes LastName, FirstName, DOB vous permettrait essentiellement de faire deux prédicats de recherche d'index sur StoreID, puis de rechercher un prédicat sur n'importe laquelle des colonnes incluses. Cela vous permettrait de couvrir toutes les permutations de recherche possibles tant qu'il commence par StoreID.

mEmENT0m0RI
la source