Si les index représentent l’optimisation la plus simple à mettre en place, on souhaite néanmoins conserver le minimum d’entre-eux, car leur maintenance lors de l’exécution de requêtes de modifications de données (INSERT, UPDATE, DELETE) peut être coûteuse, surtout sur des tables volumineuses.
Voyons comment collecter cette information …
La requête suivante retourne tous les index de la base de données en cours pour lesquels seule la maintenance a eu lieu (mais qui n’ont servi aucune requête de type SELECT (IUS.user_lookups + IUS.user_scans + IUS.user_seeks = 0):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | ------------------------------- -- Nicolas SOUQUET - 16/11/2010 ------------------------------- SELECT T.name AS table_name , PS.row_count , I.name AS index_name , dbo.nico(I.object_id, I.index_id) AS index_key , IUS.user_lookups + IUS.user_scans + IUS.user_seeks AS total_user_searches , IUS.user_updates , IUS.system_lookups + IUS.system_scans + IUS.system_seeks AS total_system_searches , IUS.system_updates FROM sys.dm_db_index_usage_stats AS IUS INNER JOIN sys.indexes AS I ON IUS.database_id = DB_ID() AND IUS.object_id = I.object_id AND IUS.index_id = I.index_id INNER JOIN sys.tables AS T ON I.object_id = T.object_id INNER JOIN sys.dm_db_partition_stats AS PS ON PS.object_id = T.object_id AND PS.index_id BETWEEN 0 AND 1 LEFT JOIN sys.objects AS O ON O.parent_object_id = I.object_id AND O.type = 'PK' AND O.name = I.name WHERE IUS.user_lookups + IUS.user_scans + IUS.user_seeks = 0 AND O.object_id IS NULL ORDER BY IUS.user_updates DESC --PS.row_count DESC |
On exclut les indexes sous-jacents à une clé primaire, puisque leur suppression obligerait celle de la contrainte.
Si l’on souhaite extraire la clé de l’index, il faudra alors créer la petite fonction suivante :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | ------------------------------- -- Nicolas SOUQUET - 16/11/2010 ------------------------------- CREATE FUNCTION dbo.ufn_GetIndexKey ( @_object_id int , @_index_id int ) RETURNS varchar(max) AS BEGIN DECLARE @index_key varchar(max) SELECT @index_key = CASE WHEN @index_key IS NULL THEN C.name ELSE @index_key + + ', ' + C.name END FROM sys.indexes AS I INNER JOIN sys.index_columns AS IC ON I.object_id = IC.object_id AND I.index_id = IC.index_id INNER JOIN sys.columns AS C ON IC.object_id = C.object_id AND IC.column_id = C.column_id WHERE I.object_id = @_object_id AND I.index_id = @_index_id ORDER BY IC.index_column_id RETURN @index_key END |
Que l’on peut utiliser directement comme suit :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | SELECT T.name AS table_name , PS.row_count , I.name AS index_name , dbo.ufn_GetIndexKey (I.object_id, I.index_id) AS index_key , IUS.user_lookups + IUS.user_scans + IUS.user_seeks AS total_user_searches , IUS.user_updates , IUS.system_lookups + IUS.system_scans + IUS.system_seeks AS total_system_searches , IUS.system_updates FROM sys.dm_db_index_usage_stats AS IUS INNER JOIN sys.indexes AS I ON IUS.database_id = DB_ID() AND IUS.object_id = I.object_id AND IUS.index_id = I.index_id INNER JOIN sys.tables AS T ON I.object_id = T.object_id INNER JOIN sys.dm_db_partition_stats AS PS ON PS.object_id = T.object_id AND PS.index_id BETWEEN 0 AND 1 LEFT JOIN sys.objects AS O ON O.parent_object_id = I.object_id AND O.type = 'PK' AND O.name = I.name WHERE IUS.user_lookups + IUS.user_scans + IUS.user_seeks = 0 AND O.object_id IS NULL ORDER BY IUS.user_updates DESC --PS.row_count DESC |
Il ne reste alors plus qu’à juger de la pertinence de la suppression de l’index, car leur création est assez coûteuse.
En effet, si on supprime un index et qu’on se rend compte qu’il aurait pu servir des requêtes :
– dans le cas où on dispose d’une édition Standard de SQL Server, on doit recréer l’index et cela bloque la table
– dans le cas où on dispose d’une édition Enterprise de SQL Server, on peut recréer l’index en ligne, mais cela utilise massivement la base de données système TempDB dans laquelle est effectuée le tri, plus l’espace disque nécessaire à la copie de l’index, et enfin le coût de maintenance de deux index au lieu d’un.
Bonne indexation !