Accueil / Ressources / Liste des index uniquement scannés

› Script SQL

Liste des index uniquement scannés.

Un index qui n'est jamais utilisé en recherche (seek) mais seulement parcouru en entier (scan) n'apporte pas le bénéfice attendu d'un index. Il sert peut-être de simple réservoir de colonnes, ou ses colonnes clés ne correspondent pas aux filtres des requêtes.


Les requêtes de cette page lisent des informations de l'instance sans modifier vos données. Les commandes qui modifient quelque chose sont signalées dans le texte. Testez toujours sur un environnement non critique avant la production, et relisez chaque requête avant de l'exécuter.

Le script

T-SQL
-- Index utilisés uniquement en parcours (scans), jamais en recherche (seeks)
-- À exécuter dans la base à analyser. Droit requis : VIEW SERVER STATE.
SET NOCOUNT ON;

SELECT
    SCHEMA_NAME(o.schema_id)                          AS schema_nom,
    o.name                                            AS table_nom,
    i.name                                            AS index_nom,
    i.type_desc                                       AS type_index,
    n.nb_lignes                                       AS lignes_table,
    u.user_scans                                      AS scans,
    u.user_seeks                                      AS seeks,
    u.user_lookups                                    AS lookups,
    u.user_updates                                    AS mises_a_jour,
    u.last_user_scan                                  AS dernier_scan,
    CONVERT(decimal(18, 2), t.taille_mo)              AS taille_mo
FROM sys.indexes AS i
JOIN sys.objects AS o
     ON o.object_id = i.object_id
    AND o.type = 'U'
    AND o.is_ms_shipped = 0
JOIN sys.dm_db_index_usage_stats AS u
     ON u.database_id = DB_ID()
    AND u.object_id   = i.object_id
    AND u.index_id    = i.index_id
CROSS APPLY (SELECT SUM(p.used_page_count) * 8 / 1024.0 AS taille_mo
             FROM sys.dm_db_partition_stats AS p
             WHERE p.object_id = i.object_id
               AND p.index_id  = i.index_id) AS t
CROSS APPLY (SELECT SUM(p.row_count) AS nb_lignes
             FROM sys.dm_db_partition_stats AS p
             WHERE p.object_id = i.object_id
               AND p.index_id IN (0, 1)) AS n
WHERE i.index_id >= 1                       -- exclut les tas (index_id = 0)
  AND u.user_seeks = 0
  AND u.user_scans > 0
ORDER BY u.user_scans DESC;

Comment lire le résultat

  • type_index CLUSTERED : un index clusterisé uniquement scanné correspond à une table lue en entier. C'est normal sur une petite table, préoccupant sur une grosse.
  • lignes_table : un scan sur quelques centaines de lignes est sans conséquence. Concentrez-vous sur les tables de plusieurs centaines de milliers de lignes ou plus.
  • mises_a_jour élevées avec peu de scans : l'index coûte plus qu'il ne sert.

Pistes d'amélioration

  • Rechercher les requêtes qui scannent cet index (plan d'exécution) : leur prédicat est-il sargable, c'est-à-dire exploitable par une recherche ?
  • Vérifier l'ordre des colonnes clés : la colonne la plus filtrée en égalité doit venir en premier.
  • Détecter les conversions implicites de types et les fonctions appliquées aux colonnes filtrées, qui empêchent la recherche.
  • Si l'index est utilisé comme index couvrant pour un rapport, un scan est acceptable : il peut alors être conservé, ou remplacé par un index columnstore pour de l'analytique.

Un incident, un audit, une question ?

Un premier échange suffit à cadrer l'urgence et le périmètre.

Demander une intervention