Technical Article

Index stats with key,included columns, size - all you can eat

,

Use myDB GO THEN run script.

Either don't specify a table and get all results for the DB or specify table at top and get results for specific table.

Play around with Where clause conditions that I have provided, to get a feel of what it does.

****

This has helped me zero in on a problem with timeouts in about 2 minutes by identifying an index which had excessive pagelockwaits+rowlockwaits.

DECLARE @dbid INT,@tablename sysname --= 'schema.table' --''
SELECT @dbid = DB_ID()
SELECT sch.name+'.'+o.name TableName, COALESCE(i.name, 'HEAP') indexName,
	COALESCE(IndexKey,'') IndexKey,S.partition_number PartNo, ps.used_page_count * 8/1024 AS IndexSizeMB,
	COALESCE(CONVERT(SMALLDATETIME, STATS_DATE(S.object_id, i.index_id)),'') LastStatsUpDate, P.Rows as [rowcount], 
	(case when fill_factor =0 then 100 else fill_factor END) as [Fillfactor],i.index_id, i.is_unique,i.is_primary_key,
	--Query Access
	Usage.user_seeks seeks, Usage.user_scans scans, Usage.user_lookups lookups,range_scan_count,
	singleton_lookup_count, Usage.user_scans *P.Rows as MaxRowsScanned , 
	--Index Changes
	S.nonleaf_update_count+s.leaf_update_count as updates, S.leaf_insert_count inserts,
	S.leaf_delete_count deletes, S.nonleaf_delete_count nonleafdel, 
	--Rowlocks
	row_lock_count RowLocks, row_lock_wait_count RwLkWtCnt,
	cast(1.0 * row_lock_wait_in_ms / ( CASE WHEN row_lock_wait_count = 0 THEN 1 ELSE row_lock_wait_count END ) as numeric(10,2)) [Avg row lock time(ms)], 
	--PageLocks
	page_lock_count,page_lock_wait_count PageLocks, page_lock_wait_in_ms ,
	cast(1.0 *page_lock_wait_in_ms / ( CASE WHEN page_lock_wait_count = 0 THEN 1 ELSE page_lock_wait_count END ) as numeric(10,2)) [Avg pg lock time(ms)],
	--PageLatchWaits
	page_latch_wait_count,page_latch_wait_in_ms,
	cast(1.0*page_latch_wait_in_ms / ( CASE WHEN page_latch_wait_count = 0 THEN 1 ELSE page_latch_wait_count END ) as numeric(10,2)) [Avg pg Latch time(ms)]
FROM	sys.dm_db_index_operational_stats(@dbid, object_id(@tablename), NULL, NULL) S
	INNER JOIN sys.partitions P on S.index_id = p.index_id AND s.object_id = p.object_id AND s.partition_number = p.partition_number
	INNER JOIN sys.indexes i ON i.object_id = S.object_id AND i.index_id = S.index_id
	INNER JOIN sys.objects o on i.object_id = o.object_id AND o.is_ms_shipped=0
	INNER JOIN sys.schemas sch on o.schema_id = sch.schema_id
	LEFT JOIN ( 
		SELECT object_id, index_id, name, LEFT(subwindow.includeKey, LEN(subwindow.includeKey) - 1) AS 'IndexKey'
			FROM ( SELECT i.index_id, i.name, object_id,
						(	SELECT c.name + ',' AS [text()]
							FROM	sys.indexes i2
								INNER JOIN sys.index_columns ic ON ic.object_id = i2.object_id AND ic.index_id = i2.index_id
								INNER JOIN sys.columns c ON c.object_id = ic.object_id AND c.column_id = ic.column_id
							WHERE	 ic.is_included_column = 0 AND
								i.object_id = i2.object_id AND
								i.index_id = i2.index_id
							ORDER BY i2.name
							FOR XML PATH('') ) 
				 + COALESCE(' INCLUDE:' +
						 (	SELECT	c.name + ',' AS [text()]
							FROM	 sys.indexes i2
								INNER JOIN sys.index_columns ic ON ic.object_id = i2.object_id AND
											ic.index_id = i2.index_id
								INNER JOIN sys.columns c ON c.object_id = ic.object_id AND
											c.column_id = ic.column_id
							WHERE	 ic.is_included_column = 1 AND
								i.object_id = i2.object_id AND
								i.index_id = i2.index_id
							ORDER BY i2.name
							FOR XML PATH('') ), '') AS includeKey
		FROM	 sys.indexes i
		) subwindow ) window ON window.object_id = i.object_id AND window.index_id = i.index_id
	INNER JOIN sys.dm_db_index_usage_stats AS Usage ON i.object_id = Usage.object_id 
													AND i.index_id = Usage.index_id AND usage.database_id = S.database_id
	INNER JOIN sys.dm_db_partition_stats PS on p.partition_id=ps.partition_id AND p.object_id = ps.object_id 
	  AND p.index_id = ps.index_id AND p.partition_number = ps.partition_number 
-------------------
WHERE s.database_id = @dbid AND sch.name+'.'+o.name = COALESCE(@tablename,sch.name+'.'+o.name)
	 --AND rows > 500000 AND user_seeks+user_scans =0 --Find large space wasters.
	 --AND row_lock_wait_count +page_lock_wait_count +page_latch_wait_count >0


---------order by	 
--ORDER BY 1 ASC,2,3,5 --alphabetically
--order by page_latch_wait_in_ms desc
order by RwLkWtCnt desc
--order by page_lock_wait_in_ms desc
--order by row_lock_wait_in_ms desc
--Order by case when user_seeks =0 then 0 else singleton_lookup_count/user_seeks END desc -- Potential Bad index seeks.
--order by MaxRowsScanned desc
--order by user_scans desc
--order by user_scans desc

Rate

3.25 (4)

You rated this post out of 5. Change rating

Share

Share

Rate

3.25 (4)

You rated this post out of 5. Change rating