Get Index Size and Counts

Applicability:

SQL Server 2000: Tested

SQL Server 2005: Tested

SQL Server 2008: Tested

SQL Server 2008R2: Tested

SQL Server 2012: Not Tested

Credits:

Author: Jeffrey Yao/ChillyDBA

Date: 27 Sep 2010

Description

From SQL 2005 onwards, the retrieval of index metadata was made more straightforward with the introduction of the sys.dm_db_partition_stats view. With SQL 2000 and prior versions, the retrieval of this information meant an iteration through the tables in a DB to collate the output from sp_spaceused.

I have listed below two versions of an SP to retrieve index stats for all tables in a database.

Code

SQL Server 2000:

DROP PROCEDURE dbo.usp_GetTableSizes_2000

Go

CREATE PROCEDURE dbo.usp_GetTableSizes_2000

AS

/**********************************************************

Purpose: To retrieve table and index sizes for all tables

in a DB. Index and Statistics (ie '_wa_%') counts and

index statistics update date are also included

Author: ChillyDBA

History: 2001 - Initial Release

***********************************************************/

CREATE TABLE #TableSizes

(

name SYSNAME,

rows INT,

reserved VARCHAR(30),

data VARCHAR(30),

index_size VARCHAR(30),

unused VARCHAR(30)

)

CREATE TABLE #TableIndexes

(

name SYSNAME,

total_indexes INT,

total_clustered INT,

total_nonclustered INT,

total_statistics INT,

stats_last_updated DATETIME

)

DECLARE

@SnapshotDate DATETIME, -- in case this information is to be persisted for trend analysis

@TableName SYSNAME,

@Cmd VARCHAR(1000)

SELECT @SnapshotDate = GETDATE()

SELECT @TableName = MIN(name)

FROM sysobjects

WHERE TYPE = 'u'

AND name NOT LIKE 'sys%'

WHILE @TableName IS NOT NULL

BEGIN

SELECT @Cmd = 'sp_spaceused ' + @TableName

INSERT #TableSizes

EXEC (@Cmd)

INSERT #TableIndexes (name, total_indexes, total_nonclustered, total_clustered, total_statistics, stats_last_updated)

SELECT @TableName,

-- get # indexes that are not system stats

(SELECT COUNT(*)

FROM sysindexes

WHERE id = OBJECT_ID(@TableName)

AND name NOT LIKE '_wa_%'

AND indid > 0),

-- get # nonclustered indexes that are not system stats

(SELECT COUNT(*)

FROM sysindexes

WHERE id = OBJECT_ID(@TableName)

AND name NOT LIKE '_wa_%'

AND indid > 1),

-- get # clustered indexes that are not system stats

(SELECT COUNT(*)

FROM sysindexes

WHERE id = OBJECT_ID(@TableName)

AND name NOT LIKE '_wa_%'

AND indid = 1),

-- get # system stats

(SELECT COUNT(*)

FROM sysindexes

WHERE id = OBJECT_ID(@TableName)

AND name LIKE '_wa_%'

AND indid > 0),

-- get date that index stats were last updated

(SELECT MAX(STATS_DATE(ID, indid))

FROM sysindexes

WHERE id = OBJECT_ID(@TableName))

SELECT @TableName = MIN(name)

FROM sysobjects

WHERE TYPE = 'u'

AND name > @TableName

AND name NOT LIKE 'sys%'

AND uid = 1

END

--insert dba_table_sizes

SELECT

DB_NAME() AS DatabaseName,

ts.name AS TableName,

-- remove the kb suffix so that these figures can be manipulated easily in excel or any other query

REPLACE(rows, 'kb','') AS #Rows,

REPLACE(reserved, 'kb','') AS KB_Reserved,

REPLACE(data, 'kb','') AS KB_Data,

REPLACE(index_size, 'kb','') AS KB_Index,

REPLACE(unused, 'kb','') AS KB_Unused,

ti.total_indexes AS #TotalIndexes,

ti.total_nonclustered AS #NonClusteredIndexes,

ti.total_clustered AS #ClusteredIndexes,

ti.total_statistics AS #Statistics,

ti.stats_last_updated AS StatsLastUpdatedDate,

DATEDIFF( dd, ti.stats_last_updated, GETDATE()) AS StatsAgeInDays,

@SnapshotDate AS SnapshotDate

FROM #TableSizes ts

INNER JOIN #TableIndexes ti

ON ts.name = ti.name

--order by cast(replace(reserved, 'kb','')as int) desc

ORDER BY ts.name

DROP TABLE #TableSizes

DROP TABLE #TableIndexes

SQL Server 2005 onwards:

DROP PROCEDURE dbo.usp_GetTableSizes_2005

Go

CREATE PROCEDURE dbo.usp_GetTableSizes_2005

AS

/**********************************************************

Purpose: To retrieve table and index sizes for all tables

in a DB. Index and Statistics (ie '_wa_%') counts and

index statistics update date are also included

Author: Jeffrey Yao

History: 27 Sep 2010 - Initial Release

19 Jun 2012 - ChillyDBA - Augmented with # coumns to match the

output with the usp_GetTableSizes_2000 SP

***********************************************************/

DECLARE

@SnapshotDate DATETIME -- in case this information is to be persisted for trend analysis

SELECT @SnapshotDate = GETDATE()

SELECT

DB_Name() AS DatabaseName,

object_schema_name(OBJECT_ID) + '.' + OBJECT_NAME(OBJECT_ID) AS TableName,

SUM(CASE WHEN index_id < 2 THEN row_count ELSE 0 END) AS #Rows,

8*SUM(reserved_page_count) AS KB_Reserved,

8*SUM

( CASE

WHEN index_id<2 THEN in_row_data_page_count

+ lob_used_page_count

+ row_overflow_used_page_count

ELSE lob_used_page_count

+ row_overflow_used_page_count

END

) AS KB_Data,

8*

(

SUM(used_page_count) - SUM

( CASE

WHEN index_id<2 THEN in_row_data_page_count

+ lob_used_page_count

+ row_overflow_used_page_count

ELSE lob_used_page_count

+ row_overflow_used_page_count

END

)

) AS KB_Index,

8*SUM(reserved_page_count-used_page_count) AS KB_Unused,

(

SELECT COUNT(*)

FROM sys.dm_db_partition_stats ps2 (NOLOCK)

WHERE ps.object_id = ps2.object_id

AND OBJECT_NAME(ps2.object_ID) NOT LIKE '_wa_%'

AND ps2.index_id > 0

) AS #TotalIndexes,

(

SELECT COUNT(*)

FROM sys.dm_db_partition_stats ps3 (NOLOCK)

WHERE ps.object_id = ps3.object_id

AND OBJECT_NAME(ps3.object_ID) NOT LIKE '_wa_%'

AND ps3.index_id > 1

) AS #TotalNonClusteredIndexes,

(

SELECT COUNT(*)

FROM sys.dm_db_partition_stats ps4 (NOLOCK)

WHERE ps.object_id = ps4.object_id

AND OBJECT_NAME(ps4.object_ID) NOT LIKE '_wa_%'

AND ps4.index_id = 1

) AS #TotalClusteredIndexes,

(

SELECT COUNT(*)

FROM sysindexes si (NOLOCK)

WHERE ps.object_id = si.id

AND si.name LIKE '_wa_%'

AND si.indid > 0

) AS #Statistics,

(

SELECT MAX(STATS_DATE(object_id, index_id))

FROM sys.dm_db_partition_stats ps6 (NOLOCK)

WHERE ps.object_id = ps6.object_id

) AS StatsLastUpdatedDate,

DATEDIFF( dd, (

SELECT MAX(STATS_DATE(object_id, index_id))

FROM sys.dm_db_partition_stats ps6 (NOLOCK)

WHERE ps.object_id = ps6.object_id

), GETDATE()) AS StatsAgeInDays,

@SnapshotDate AS SnapshotDate

FROM sys.dm_db_partition_stats ps (NOLOCK)

WHERE OBJECT_ID > 1024

GROUP BY OBJECT_ID

ORDER BY TableName