See Also: Main_Page - Transact SQL Code Library - Index Performance Tuning
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.s3.amazonaws.com/SQLServerPedia-20090324-IndexTuning1.flv
, 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)
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
2009-07-04 - Brent Ozar - now filters out duplicate rows for partitioned tables.