SQL Server中检查和移除无用索引

SQL Server是微软公司出品的数据库软件,它有很强大的索引优化功能,只需要将待执行的SQL语句放入到执行计划分析中,就可以给出缺失索引的建议。

但因此又带来另一个问题,随着系统的不停发展,之前经常用的SQL可能就不用了,那么相应建立的索引也就没有用了,无用索引一旦多起来,不但存储空间浪费,也会影响插入更新删除的性能问题,因此建议也要定期检查和移除无用索引。

我们可以用如下SQL语句来查询出无用索引:

SELECT 'SQL Server Instance Start with ' + CONVERT(VARCHAR(16),create_date,120)  FROM sys.databases
WHERE database_id =2;
    
SELECT  DB_NAME(diu.database_id)                  AS DatabaseName ,
        s.name +'.' +QUOTENAME(o.name)            AS TableName    ,
        i.index_id                                AS IndexID   ,
        i.name                                    AS IndexName        ,
        CASE WHEN i.is_unique =1 THEN 'UNIQUE INDEX'
           ELSE 'NOT UNIQUE INDEX'    END         AS IS_UNIQUE,
        CASE WHEN i.is_disabled=1 THEN 'DISABLE'
           ELSE 'ENABLE'            END           AS IndexStatus,
        o.create_date                             AS IndexCreated,
        STATS_DATE(o.object_id,i.index_id)        AS StatisticsUpdateDate,
        diu.user_seeks                            AS UserSeek ,
        diu.user_scans                            AS UserScans ,
        diu.user_lookups                          AS UserLookups ,
        diu.user_updates                          AS UserUpdates ,
        p.TableRows ,
        'DROP INDEX ' + QUOTENAME(i.name) 
        + ' ON ' + QUOTENAME(s.name) + '.'
        + QUOTENAME(OBJECT_NAME(diu.object_id)) +';' AS 'Drop Index Statement'
FROM    sys.dm_db_index_usage_stats diu
        INNER JOIN sys.indexes i ON i.index_id = diu.index_id
                                    AND diu.object_id = i.object_id
        INNER JOIN sys.objects o ON diu.object_id = o.object_id
        INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
        INNER JOIN ( SELECT SUM(p.rows) TableRows ,
                            p.index_id ,
                            p.object_id
                     FROM   sys.partitions p
                     GROUP BY p.index_id ,
                            p.object_id
                   ) p ON p.index_id = diu.index_id
                          AND diu.object_id = p.object_id
WHERE   OBJECTPROPERTY(diu.object_id, 'IsUserTable') = 1
        AND diu.database_id = DB_ID()
        AND i.is_primary_key = 0        --排除主键索引
        AND i.is_unique_constraint = 0         --排除唯一索引
        AND diu.user_updates <> 0              --排除没有数据变化的索引
        AND diu.user_lookups = 0
        AND diu.user_seeks = 0
        AND diu.user_scans = 0
        AND i.name IS NOT NULL                 --排除那些没有任何索引的堆表
ORDER BY ( diu.user_seeks + diu.user_scans + diu.user_lookups ) ASC,diu.user_updates DESC;

它主要利用了sys.dm_db_index_usage_stats这个索引使用的统计来检查哪些索引从未被使用。

请注意,新建的索引,因统计时间短,不建议删除。

另外主键索引、唯一索引、用户从来不更新的索引,这三类并不在上述优化范围内,需要自行人工处理。