ND i.index_id = ddius.index_id
WHERE OBJECTPROPERTY(ddius.[object_id], 'IsUserTable') = 1
AND ddius.database_id = DB_ID()
AND OBJECT_NAME(ddius.[object_id])='表名'
AND i.index_id > 1 --非聚集索引
ORDER BY [Difference] DESC ,
[Total Writes]DESC ,
[Total Reads]ASC;
?
我们这里主要关注非聚集索引,因为绝大部分情况下,聚集索引是主键,在系统运行了一段时间后,你能修改主键的可能已经大大降低,并且主键一般问题不大。下面是语句执行的大概样子:

?
得到了这些信息之后,就开始做初步分析,对于大部分系统而言,读操作远大于写操作,所以如果你的系统也是类似的,那么可以选出上图中【Total Reads】远小于【Total Writes】的那些索引进行优先分析对象,如上图的第五个索引。
?
某个索引被使用的具体情况:
再次说明,很多方法可以实现这种分析,在不需要深入研究的情况下,够用就好。本人通过改写国外大牛的一个关于查找“并行执行语句”的脚本,实现获取某个索引自实例启动依赖被使用的具体情况。原脚本如下:
?
--执行计划中使用了并行操作的语句:
SET TRANSACTION ISOLATIONLEVEL READ UNCOMMITTED;
WITH XMLNAMESPACES(DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT COALESCE(DB_NAME(p.dbid)
, p.query_plan.value('(//RelOp/OutputList/ColumnReference/@Database)[1]','nvarchar(128)'))
AS database_name
,DB_NAME(p.dbid) + '.' + OBJECT_SCHEMA_NAME(p.objectid, p.dbid)
+ '.' + OBJECT_NAME(p.objectid, p.dbid) AS object_name
,cp.objtype
,p.query_plan
,cp.UseCounts AS use_counts
,cp.plan_handle
,CAST('
' AS XML) AS sql_text
FROM sys.dm_exec_cached_plans cp
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) p
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) AS q
WHERE cp.cacheobjtype = 'Compiled Plan'
AND p.query_plan.exist('//RelOp[@Parallel = "1"]') = 1
ORDER BY COALESCE(DB_NAME(p.dbid), p.query_plan.value('(//RelOp/OutputList/ColumnReference/@
Database)[1]','nvarchar(128)')), UseCountsDESC
?
下面是本人改写后的脚本:
?
--获取某个索引被使用的情况
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
WITH XMLNAMESPACES(DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT COALESCE(DB_NAME(p.dbid)
,p.query_plan.value('(//RelOp/OutputList/ColumnReference/@Database)[1]','nvarchar(128)'))
ASdatabase_name
,DB_NAME(p.dbid) + '.' + OBJECT_SCHEMA_NAME(p.objectid, p.dbid)
+'.' + OBJECT_NAME(p.objectid, p.dbid) AS OBJECT_NAME,
cast ('索引名' as varchar(64)) AS IndexName
,cp.objtype
,p.query_plan
,cp.UseCounts AS use_counts
,cp.plan_handle
,CAST('
' AS XML) AS sql_text INTO xxx.xxx.xxx表
FROM sys.dm_exec_cached_plans cp
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) p
CROSS APPLY sys.dm_exec_sql_text(cp.plan_handle) AS q
WHERE cp.cacheobjtype = 'Compiled Plan'
AND p.query_plan.exist('//Object[@Index = "[索引名]"]') = 1
ORDER BY UseCounts DESC,COALESCE(DB_NAME(p.dbid), p.query_plan.value('(//RelOp/OutputList/ColumnReference/@
Database)[1]','nvarchar(128)'))
?
脚本有两个注意的地方:
1.需要手动替换语句中的“索引名”三个字,共两处地方。另外要注意的是,这个语句查询的是XML格式的执行计划,XML是大小写区分的,所以要严格按照索引名(在SSMS中查到的名字)来替换,否则查询不出来。另外针对索引名,本系统就出现过不同的表使用了相同的索引名(如index1),这种极其乱来的命名规则应该避免。这种情况也导致了你分析的索引可能是另外一个表的,所以需要再次检查。
2.脚本中有INTO xxx.xxx.xxx表的部分。这个是本人的监控习惯,在同一个服务器上,若有空间和条件,建议创建一个独立的数据库(简单模式即可),存储所有你感兴趣的数据库运维和性能信息,以便后续之用。这个如果不需要存储,记得注释掉。
?
下面是脚本结果的示例:

?
接上图

?
现在来解释一下这个结果:
?
Database_name:指使用这个索引的对象(如存储过程)所在的数据库,本
系统存在跨库操作的行为,所以一个索引可能被多个数据库的对象使用。这个可以用于找到对象所在的数据库,如果有必要可以对这个数据库做进一步分析,如这个数据库的配置情况等。OBJECT_NAME:这个是使用索引的对象名,如果是数据库内存储的对象(如存储过程、函数、视图等),这里会有结果,如果是动态SQL,此处为NULL。IndexName:索引名字,一般仅用来后续统计之用。Objtype:使用索引的对象类型:Proc为存储过程,Prepared为预定义语句,详细类型说明可以查看联机丛书关于s