web-dev-qa-db-fra.com

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

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

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

- OR -

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

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

411
Cory

Si la colonne ne figure pas dans le WHERE/JOIN/GROUP BY/ORDER BY, mais uniquement dans la liste des colonnes de la clause SELECT.

La clause INCLUDE ajoute les données au niveau le plus bas/au niveau 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 columns ne sont pas des colonnes de clé dans l'index, elles ne sont donc pas ordonnées. Cela signifie que ce n'est pas vraiment utile pour les prédicats, le tri, etc. comme je l'ai mentionné ci-dessus. Cependant, il peut être utile si vous avez une recherche résiduelle dans quelques lignes de la ou des colonnes de clé.

n autre article MSDN avec un exemple concret

347
gbn

Vous utiliseriez INCLUDE pour ajouter une ou plusieurs colonnes au niveau feuille d'un index non cluster, si vous pouviez ainsi "couvrir" vos requêtes.

Imaginez que vous ayez besoin de rechercher l'ID d'un employé, l'ID de 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 d'un service donné, vous devez maintenant effectuer une "recherche de signet" pour obtenir l'enregistrement complet complet de l'employé, juste pour obtenir la colonne du dernier nom. . Cela peut coûter très cher en performances si vous rencontrez 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)

alors toutes les informations dont vous avez besoin sont disponibles dans le niveau feuille de l'index non clusterisé. Simplement en cherchant dans l'index non groupé et en recherchant vos employés pour un service donné, vous disposez de 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.

Évidemment, vous ne pouvez pas inclure chaque colonne dans chaque index non clusterisé - mais si vous avez des requêtes pour lesquelles une ou deux colonnes manquent pour être "couvertes" (et qui sont beaucoup utilisées), il peut être très utile d’INCLURE celles-ci. dans un index approprié non-cluster.

210
marc_s

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" en tant que index - colonnes ou en tant que inclus - colonnes .

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

  1. Utilisez index sur id1, id2 ... idN seul ou
  2. Utiliser l'index sur id1, id2 ... idN plus include 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 not utilisées dans des restrictions

(La possibilité d'inclure toutes ces colonnes dans l'index-clé est toujours idiote (sauf si elles sont également utilisées dans des restrictions) - car il serait toujours plus coûteux de le gérer, car l'index doit être mis à jour et trié même lorsque le "clés" n'ont pas changé).

Alors utilisez l'option 1 ou 2?

Réponse: Si votre table est rarement mise à jour - la plupart du temps insérée dans/supprimée -, il est relativement peu coûteux d’utiliser le mécanisme include pour inclure des "colonnes chaudes" (souvent utilisées dans les sélections - mais not = souvent utilisé dans les restrictions) car les insertions/suppressions nécessitent de toute façon que l’index soit mis à jour/trié; ainsi, peu de charge supplémentaire est associée au stockage de quelques colonnes supplémentaires alors que l’index est déjà mis à jour. Le temps système représente la mémoire supplémentaire et le processeur utilisés pour stocker des informations redondantes sur l'index.

Si les colonnes que vous envisagez d'ajouter comme colonnes incluses sont souvent mises à jour (sans l'index -clé - les colonnes sont mises à jour) - o - s'il y en a tellement que l'index devient proche d'une copie de votre table - utilisez l'option 1, je vous le suggère! De même, si l'ajout de certaines colonnes d'inclusion ne fait aucune différence en termes de performances, vous pouvez ignorer l'idée de les ajouter :) Vérifiez qu'elles sont utiles!

Le nombre moyen de lignes par valeurs identiques 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 inclus - colonne d'index - est utilisée dans restriction: Tant que l'index en tant que tel peut être utilisé (basé sur la restriction par rapport à l'index -clé - colonnes) - SQL Server applique la restriction de la colonne à l'index (valeurs de noeud feuille) au lieu de procéder de manière coûteuse au table elle-même.

25
Fredrik Solhaug

Les colonnes d'index de base sont triées, mais les colonnes incluses ne sont pas triées. Cela économise des ressources pour la maintenance 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 les requêtes, vous pouvez définir les critères de recherche pour localiser les lignes dans les colonnes triées de l'index, mais "inclure" les colonnes supplémentaires, non triées, avec des données non recherchées. Cela aide certainement à réduire la quantité de tri et de fragmentation dans la maintenance des index.

17
onupdatecascade

Les raisons pour lesquelles (y compris les données dans le niveau feuille de l’index) ont été bien expliquées. La raison pour laquelle vous agissez deux fois à ce sujet 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 accéder à l'index clusterisé 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 tampon doit être spécifique) au fur et à mesure que les nouvelles pages de données sont chargées en mémoire, poussant potentiellement d'autres données plus souvent hors du cache tampon.

6
mrdenny

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 comme colonnes d'index, telles que varchar (max).

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

5
Robin Hames

La taille totale de toutes les colonnes insérées dans la définition d'index est limitée. Cela dit, je n’ai jamais eu à créer un index aussi large. Pour moi, le plus gros avantage est le fait que vous pouvez couvrir plus de requêtes avec un index qui inclut des colonnes car elles ne doivent pas être définies dans un ordre particulier. Pensez à est comme un index dans l'index. Un exemple serait le StoreID (où StoreID a une faible sélectivité, ce qui signifie que chaque magasin est associé à de nombreux clients), puis les données démographiques du client (Nom, Prénom, Date de naissance): si vous insérez simplement ces colonnes dans cet ordre (StoreID, Nom , Prénom, date de naissance), vous ne pouvez rechercher efficacement que les clients pour lesquels vous connaissez StoreID et LastName.

D'autre part, définir l'index sur StoreID et inclure les colonnes LastName, FirstName, DOB vous permettrait essentiellement de créer deux prédicats de recherche-index sur StoreID, puis de rechercher un prédicat sur l'une des colonnes incluses. Cela vous permettrait de couvrir toutes les permutations de recherche possibles tant que cela commence par StoreID.

2
mEmENT0m0RI

Une des raisons de préférer INCLUDE aux colonnes de clé si vous n'avez pas besoin de cette colonne dans la clé est de la documentation. Cela rend les index évolutifs beaucoup plus faciles à l’avenir.

Compte tenu de votre exemple:

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

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

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

Bien sûr, vous ne devriez pas mettre de colonnes dans INCLUDE si vous pouvez obtenir un avantage supplémentaire de les avoir dans la partie clé. Les deux requêtes suivantes préfèrent en réalité la colonne col2 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 soit pas le cas et que nous ayons col2 dans la clause INCLUDE car il n’ya aucun avantage à l’avoir. dans l'arborescence de l'index.

Avance rapide quelques années.

Vous devez ajuster cette requête:

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

Pour optimiser cette requête, l'index suivant serait génial:

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)

Vous savez maintenant que Col2 et Col3 ne font pas partie de l'arborescence d'index et ne sont donc pas utilisés pour réduire la plage d'index de lecture ni pour ordonner 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 risque de casser quelque chose:

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

Cet indice deviendra plus grand, ce qui présente encore certains risques, mais il est généralement préférable d’étendre les indices existants par rapport à l’introduction de nouveaux.

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

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

Que se passe-t-il si vous ajoutez another_col entre Col1 et Col2? Est-ce que d'autres requêtes vont en souffrir?

Il existe d'autres "avantages" de INCLUDE par rapport aux colonnes de clé si vous ajoutez ces colonnes simplement pour éviter de les extraire de la table . Cependant, je considère l'aspect documentation le plus important.

Pour répondre à ta question:

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

Si vous ajoutez une colonne à l'index uniquement pour la rendre disponible dans l'index sans consulter la table, placez-la dans la clause INCLUDE.

Si l'ajout de la colonne à la clé d'index apporte des avantages supplémentaires (par exemple pour order by ou parce qu'il peut réduire la plage d'index de lecture), ajoutez-le à 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

1
Markus Winand