Continuing the indexing theme, today we will be looking at a Dynamic Management Function (still lumped in as a DMV if you are new to these) called sys.dm_db_index_physical_stats. It is the first step down your maintenance path, if you consider my last post as a fork in the road for deciding whether or not the index should be kept.
We interact with the function as you would any other table valued function. The function takes the following parameters:
- database_id | NULL | 0 | DEFAULT
- If NULL is specified all databases are returned
- If you specify NULL for database_id you must also specify NULL for object_id, index_id, and partition_number
- object_id | NULL | 0 | DEFAULT
- If NULL is specified all table objects are returned
- If you specify NULL for object_id you must also specify NULL for index_id and partition_number
- index_id | 0 | NULL | -1 | DEFAULT
- If NULL is specified all indexes are returned
- If you specify NULL for index_id you must also specify NULL for partition_number
- partition_number | NULL | 0 | DEFAULT
- mode | LIMITED | SAMPLED | DETAILED
Here are a few examples of differing mode values and their uses:
LIMITED
If you are primarily interested in fragmentation levels use the LIMITED mode.
SELECT *
FROM sys.dm_db_index_physical_stats(db_id(), null, null, null, 'LIMITED');
This is the most simple method of interaction, with only a database ID being passed and LIMITED scan level. This will return all the objects in the database with the following output:
Note, in the LIMITED scan level the columns avg_page_space_used_in_percent, record_count, ghost_record_count, version_ghost_record_count, min_record_size_in_bytes, max_record_size_in_bytes, avg_record_size_in_bytes, forwarded_record_count, and compressed_page_count are always NULL.
SAMPLED
If you are interested in the effectiveness of PAGE compression, you can execute the following query:
SELECT DB_NAME(s.database_id) AS DatabaseName
,OBJECT_NAME(s.[object_id]) AS ObjectName
,i.name
,s.partition_number
,s.index_type_desc
,s.alloc_unit_type_desc
,s.index_depth
,s.page_count
,s.compressed_page_count
FROM sys.dm_db_index_physical_stats(db_id(), null, null, null, 'SAMPLED') AS s
INNER JOIN sys.indexes AS i ON i.[object_id] = s.[object_id]
AND i.index_id = s.index_id;
DETAILED
If you are interested in getting details on disk space usage you can execute the following query:
SELECT DB_NAME(s.database_id) AS DatabaseName
,OBJECT_NAME(s.[object_id]) AS ObjectName
,i.name
,s.partition_number
,s.index_type_desc
,s.alloc_unit_type_desc
,s.index_depth
,s.page_count
,s.avg_page_space_used_in_percent
FROM sys.dm_db_index_physical_stats(db_id(), null, null, null, 'DETAILED') AS s
INNER JOIN sys.indexes AS i ON i.[object_id] = s.[object_id]
AND i.index_id = s.index_id;
There are a few other points to be aware of with this function:
- As your mode changes (LIMITED—>SAMPLED—>DETAILED) your cost for execution climbs. This is due to further levels of the index being traversed. Be extremely careful about when you execute this in a production environment.
- Be as specific as possible with your parameters. If you can achieve your result with less scans, the better you are for it.
- Don’t take the results as a silver bullet. For certain usage patterns fragmentation isn’t reason enough to rebuild/reorganize an index.
Next up…part 3 of course! :)
29e8e886-c18b-4975-9fdd-0e89b1e95e19|1|5.0