Une procédure pour connaître l’état physique et l’utilisation des index

Voici une petite procédure stockée qui permet de connaître l’état physique des index (nombre de pages du niveau feuille, fragmentation et taux d’utilisation des pages) en même temps que la façon dont ils sont utilisés (nombres de seeks et de scans, …).

Elle est utilisable pour collecter ces statistiques sur l’ensemble d’une base de données, ou bien sur une table en particulier

Voici le code :

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
-------------------------------
-- Nicolas SOUQUET - 04/05/2010
-------------------------------
CREATE PROCEDURE ps_get_index_stats
  @_table_name sysname = NULL
AS
BEGIN
  SET NOCOUNT ON
 
  -- Si le nom de la table n'exsite pas,
  -- alors cela revient à passer NULL en object_id à la fonction sys.dm_db_index_physical_stats
  -- ce qui a pour effet de collecter toutes les statististiques de tous les index de la BD
  IF
  (
    @_table_name IS NOT NULL
    AND NOT EXISTS
    (
      SELECT  *
      FROM  sys.tables
      WHERE  name = @_table_name
    )
  )
  BEGIN
    RAISERROR('The table %s does not exist', 16, 1, @_table_name)
    RETURN
  END
 
  SELECT    D.name AS database_name
      , O.name AS table_name
      , I.name AS index_name
      , CASE IPS.index_type_desc
        WHEN 'CLUSTERED INDEX' THEN 'CL'
        WHEN 'NONCLUSTERED INDEX' THEN 'N-CL'
        ELSE IPS.index_type_desc
      END AS index_type
      , IUS.user_updates
      , IUS.user_seeks
      , IUS.user_scans
      , IUS.last_user_seek
      , IUS.last_user_scan
      , IPS.index_depth
      , IPS.page_count
      , CAST(IPS.avg_fragmentation_in_percent AS decimal(5, 2)) AS frag
      , CAST(IPS.avg_page_space_used_in_percent AS decimal(5, 2)) AS page_usage
      , STATS_DATE(IPS.object_id, IPS.index_id) AS last_update
  FROM    sys.dm_db_index_physical_stats
      (
        DB_ID() -- Base de données
        , OBJECT_ID(@_table_name) -- Table
        , NULL -- Index
        , NULL -- Partition
        , 'DETAILED' -- Niveau de détail
      ) IPS
  LEFT JOIN  sys.dm_db_index_usage_stats AS IUS
        ON IUS.database_id = IPS.database_id
        AND IUS.object_id = IPS.object_id
        AND IUS.index_id = IPS.index_id
  INNER JOIN  sys.databases AS D
        ON D.database_id = IPS.database_id
  INNER JOIN  sys.indexes AS I
        ON I.object_id = IPS.object_id
        AND I.index_id = IPS.index_id
  INNER JOIN  sys.objects AS O
        ON O.object_id = IPS.object_id
  WHERE    IPS.index_level = 0
  AND    IPS.alloc_unit_type_desc <> 'LOB_DATA'
END

Et deux exemples d’utilisation :

=> EXEC ps_get_index_stats

Collecte les statistiques de tous les index de la base de données

=> EXEC ps_get_index_stats 'maTable'

Collecte les statistiques de tous les index de la table maTable

Vous pouvez bien sûr modifier son code pour pouvoir filtrer par base de données, par index et par partition.

Attention en revanche, car si l’on passe des paramètres incorrects (valeurs négatives ou pas de valeur (NULL)) aux fonctions DB_ID() et OBJECT_ID(), cela a pour effet de collecter toutes les statistiques avec comme granularité la plus élevée.

On peut utiliser ce script pour décider de la reconstruction ou de la réorganisation d’un index …

Bonne indexation ;)

ElSüket

Laisser un commentaire