Deeper insight into unused indexes for SQL Server

ProblemOne of the balancing acts of SQL Server is the use of indexes. Too few indexes can cause scans to occur which hurts performance and too many indexes causes overhead for index maintenance during data updates and also a bloated database. So what steps can be taken to determine which indexes are being used and how they are being used.

SolutionIn a previous tip, How to get index usage information in SQL Server, we talked about how to get index usage information by using the DMVs sys.dm_db_index_operational_stats and sys.dm_db_index_usage_stats. The one issue with these views is that it only gives you part of the picture and you still need to dig deeper to get all of the information you need to determine which indexes are not used.

In this tip we will take it a step further and provide some additional queries that will shed some much needed light on the issue.

We will be using the DMV sys.dm_db_index_usage_stats which keeps track of each index that has been used and how it has been used. This was covered in the tip mentioned above. These stats are collected whenever an object is accessed. SQL Server resets these values if SQL Server is restarted or if you detach and reattach the database.

There are seven queries below and each one builds upon the others to give you more and more information. If you can't wait you can just skip right to Query 7. To select the code you should be able to triple click in the table cell to select all of the code.

Note: the information below was collected by restarting SQL Server and then doing selected queries on the AdventureWorks database. For a more heavily used database your numbers and index usage will be much higher.

Query 1

In this first query we are just using sys.dm_db_index_usage_stats and sys.objects to get a list of the indexes that have been used and how they are being used.

In this query we are listing each user table, all of its indexes and the columns that make up the index. The issue with this query is that you have a row for each column in the index which could get confusing if you have a lot of indexes.

In this query we use most of Query 3, but we are doing a PIVOT so we can see the index and the index columns in one row. This only accounts for 7 index columns, but it could easily be increased to handle more in the PIVOT operation. Here is another tip related to the use of PIVOT, Crosstab queries using PIVOT in SQL Server 2005. if you would like to better understand how PIVOT works.

This last query allow us to see both used and unused indexes. Since the DMV sys.dm_db_index_usage_stats only tracks when an index is used it is hard to compare the used and unused indexes. The query below allows you to see all indexes to compare both used and unused indexes since the stats were collected by using a UNION.

The above queries should give you a jump start to determine how indexes are being used and which indexes are being used. This will allow you to remove unused indexes as well as look for duplicate indexes that can be removed.

Based on the information collected you can determine which indexes can safely be dropped. Just make sure you collect stats long enough to have a good sampling of queries that are run against your database.

Now that you know how to determine which unused indexes you can get rid of, stay tuned for future tips on how to determine which indexes are needed

Last Update: 7/22/2008

About the author

Greg Robidoux is the President of Edgewood Solutions and a co-founder of MSSQLTips.com.

First, I am grateful of Greg forposting this great article. I have implemented and am storing all the information into a table for continous usage info, but I like to understand how to compensate for a bounce of the server or attach/detach as DMV is going to be reset? My heap is going to have all the information before a bounce takes place, but how do I use this information continously after the bounce? Appreciate an answer. Regards!

I have also heard that even though it seems an index is not used (counters 0 all over) system performance goes down once an index classified as unused is removed...therefore somehow in the back the index seems to be used even though it states it is not being used...not sure but maybe the index is used in a way that does not trigger the tracking of its usage.. Anyone with simillar expierence?

Very valuable, thank you. I have a number of databases with the same structure and indexes. Is there a way I can run the following query on all databases? I think I need to join to master..sysdatabases where databasename like 'McBam%' , but how do I do that?