Part of keeping a SQL Server database running smoothly is to keep
indexes well tuned. As database schemas or usage change, indexes that
were once important may fall out of use. Eliminating unnecessary
indexes helps speed up inserts/updates/deletes, and it saves disk space.
Starting in SQL Server 2005, Microsoft introduced some very helpful
dynamic management views (dmv), including one that tracks index usage
. It records the number of seeks and scans each
index has received since the database came online. By looking at
indexes with very low seek/scan rates, you can quickly find indexes that
are candidates for removal.
The situation is murkier when you find indexes that you believe are bad,
but the dmv’s show they are being accessed. For example, you might have
an index that includes a column that is not regularly queried with, and
you want to remove it or replace it with a more helpful column. Most
likely, the index is being used because its other columns still make it
a good choice for certain queries, but there is always the possibility
that some obscure query needs that data and will suffer a major
performance penalty if the column is removed.
The only way to assess the risk is to go and find the queries that are
making use of the index and see if they actually need the column you
think you can eliminate. The question is, how do you find them?
Back in the SQL Server 2000 days, the profiler had an option that would
like you query against the text inside of query execution plans. You
could set it up with a filter that matched the name of your index, and
then start capturing. This worked pretty well, but you had to be
confident that you were capturing enough data to really find all of the
examples of usage.
With SQL Server 2008, this profiling trick doesn’t work anymore, but
there is another way. SQL Server maintains a plan cache for queries it
has executed. Using a couple of dmv’s, you can search for query plans
that use your index and then look at them to see if they would be
affected by your proposed change.
Below is how to do it. There are a couple of flavors of this query out
there on the internet. The one below is adapted from a couple of
sources that I can no longer pinpoint, but if you want more examples of
these types of queries, check out Jason Strate’s blog.
DECLARE @IndexName sysname = 'Idx_SomeTableName_SomeIndex'; WITH XMLNAMESPACES ('http://schemas.microsoft.com/sqlserver/2004/07/showplan' as p) SELECT DB_NAME(dbid) + '.' + OBJECT_SCHEMA_NAME(objectid, dbid) + '.' + OBJECT_NAME(objectid, dbid) AS database_object ,iobj.value('@Index','sysname') AS IndexName ,cp.plan_handle ,iobj.query('.') AS IndexUsage ,qp.query_plan ,cp.plan_handle FROM sys.dm_exec_cached_plans cp CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) qp CROSS APPLY query_plan.nodes('//p:RelOp') IndexScan(scan) CROSS APPLY scan.nodes('//p:Object') as IndexObject(iobj) WHERE iobj.value('@Index','nvarchar(max)') = QUOTENAME(@IndexName,'[')
This query will then scan the procedure cache and return query plans
that contained the index in question:
Clicking on one of the plans will pull up the plan view, along with the
sql statements. Now you just need to find the index and see how it is
If you none of the plans would be affected by your proposed changes,
then you can be more confident that your index modification will not
adversely impact performance.
Of course, nothing is guaranteed. These are just the plans in cache,
and it is possible that a rare query will come along need your data. So
as always, proceed cautiously.