Find Indexes Not In Use
This query can help identify indexes that have not been used since the last restart. You can also switch the sort order to see your heavily used indexes.
The "reads_per_write" field helps to find indexes that aren't helping to improve performance. For every 1 write to the index, you want to see as many reads as possible. Indexes with a reads_per_write score of 1 mean that for every 1 write, the index is also used 1 time to help with performance. Ideally, you want to see scores much higher than that. Consider dropping indexes with a reads_per_write score under zero, and strongly consider dropping ones with scores under .1.
This isn't a hard-and-fast rule: for example, you may have an index that's only used once per month for a single report, but that report is run by the CEO and he wants it instantaneously. Before dropping indexes, know what they're used for, or make sure alternate indexes exist. Alternate indexes would be indexes that are wider than the index you're dropping, and include enough fields to serve the query's needs.
In this short tutorial video, Brent Ozar explains how to use this code to tune your environment. http://tutorials.sqlserverpedia.com/SQLServerPedia-20090324-IndexTuning1.flv
SELECT o.name , indexname=i.name , i.index_id , reads=user_seeks + user_scans + user_lookups , writes = user_updates , rows = (SELECT SUM(p.rows) FROM sys.partitions p WHERE p.index_id = s.index_id AND s.object_id = p.object_id) , CASE WHEN s.user_updates < 1 THEN 100 ELSE 1.00 * (s.user_seeks + s.user_scans + s.user_lookups) / s.user_updates END AS reads_per_write , 'DROP INDEX ' + QUOTENAME(i.name) + ' ON ' + QUOTENAME(c.name) + '.' + QUOTENAME(OBJECT_NAME(s.object_id)) as 'drop statement' FROM sys.dm_db_index_usage_stats s INNER JOIN sys.indexes i ON i.index_id = s.index_id AND s.object_id = i.object_id INNER JOIN sys.objects o on s.object_id = o.object_id INNER JOIN sys.schemas c on o.schema_id = c.schema_id WHERE OBJECTPROPERTY(s.object_id,'IsUserTable') = 1 AND s.database_id = DB_ID() AND i.type_desc = 'nonclustered' AND i.is_primary_key = 0 AND i.is_unique_constraint = 0 AND (SELECT SUM(p.rows) FROM sys.partitions p WHERE p.index_id = s.index_id AND s.object_id = p.object_id) > 10000 ORDER BY reads
Query Test Checklist
Tests Updated by Brent Ozar, 2009-04-01
2009-07-04 - Brent Ozar - now filters out duplicate rows for partitioned tables.