Compatible from SQL Server 2008
Get information about all existing indexes in given database. This script can be handful when you want to check all indexes with their create scripts and some of useful information about user statistics, basic configuration, etc. all of that in one table. You can process that data further in temp table and so on. Really handful and I’m using it almost in every performance investigation.
TIP: Use that if you want to recreate replication and you have some additional indexing on subscriber.
Script source: https://littlekendra.com/2016/05/05/how-to-script-out-indexes-from-sql-server/
SELECT DB_NAME() AS database_name, sc.name + N'.' + t.name AS table_name, ( SELECT MAX(user_reads) FROM ( VALUES (last_user_seek), (last_user_scan), (last_user_lookup) ) AS value(user_reads) ) AS last_user_read, last_user_update, 'IF NOT EXISTS (SELECT * FROM sys.indexes WHERE name=''' + si.name + ''' AND object_id = OBJECT_ID(''' + sc.name + '.' + t.name + '''))' AS checking_statement, CASE si.index_id WHEN 0 THEN N'/* No create statement (Heap) */' ELSE CASE is_primary_key WHEN 1 THEN N'ALTER TABLE ' + QUOTENAME(sc.name) + N'.' + QUOTENAME(t.name) + N' ADD CONSTRAINT ' + QUOTENAME(si.name) + N' PRIMARY KEY ' + CASE WHEN si.index_id > 1 THEN N'NON' ELSE N'' END + N'CLUSTERED ' ELSE N'CREATE ' + CASE WHEN si.is_unique = 1 THEN N'UNIQUE ' ELSE N'' END + CASE WHEN si.index_id > 1 THEN N'NON' ELSE N'' END + N'CLUSTERED ' + N'INDEX ' + QUOTENAME(si.name) + N' ON ' + QUOTENAME(sc.name) + N'.' + QUOTENAME(t.name) + N' ' END + /* key def */ N'(' + key_definition + N')' + /* includes */ CASE WHEN include_definition IS NOT NULL THEN N' INCLUDE (' + include_definition + N')' ELSE N'' END + /* filters */ CASE WHEN filter_definition IS NOT NULL THEN N' WHERE ' + filter_definition ELSE N'' END + /* with clause - compression goes here */ CASE WHEN row_compression_partition_list IS NOT NULL OR page_compression_partition_list IS NOT NULL THEN N' WITH (' + CASE WHEN row_compression_partition_list IS NOT NULL THEN N'DATA_COMPRESSION = ROW ' + CASE WHEN psc.name IS NULL THEN N'' ELSE + N' ON PARTITIONS (' + row_compression_partition_list + N')' END ELSE N'' END + CASE WHEN row_compression_partition_list IS NOT NULL AND page_compression_partition_list IS NOT NULL THEN N', ' ELSE N'' END + CASE WHEN page_compression_partition_list IS NOT NULL THEN N'DATA_COMPRESSION = PAGE ' + CASE WHEN psc.name IS NULL THEN N'' ELSE + N' ON PARTITIONS (' + page_compression_partition_list + N')' END ELSE N'' END + N')' ELSE N'' END + /* ON where? filegroup? partition scheme? */ ' ON ' + CASE WHEN psc.name IS NULL THEN ISNULL(QUOTENAME(fg.name), N'') ELSE psc.name + N' (' + partitioning_column.column_name + N')' END + N';' END AS index_create_statement, si.index_id, si.name AS index_name, partition_sums.reserved_in_row_GB, partition_sums.reserved_LOB_GB, partition_sums.row_count, stat.user_seeks, stat.user_scans, stat.user_lookups, stat.user_updates, partition_sums.partition_count, si.allow_page_locks, si.allow_row_locks, si.is_hypothetical, si.has_filter, si.fill_factor, si.is_unique, ISNULL(pf.name, '/* Not partitioned */') AS partition_function, ISNULL(psc.name, fg.name) AS partition_scheme_or_filegroup, t.create_date AS table_created_date, t.modify_date AS table_modified_date FROM sys.indexes AS si INNER JOIN sys.tables AS t ON si.object_id = t.object_id INNER JOIN sys.schemas AS sc ON t.schema_id = sc.schema_id LEFT JOIN sys.dm_db_index_usage_stats AS stat ON stat.database_id = DB_ID() AND si.object_id = stat.object_id AND si.index_id = stat.index_id LEFT JOIN sys.partition_schemes AS psc ON si.data_space_id = psc.data_space_id LEFT JOIN sys.partition_functions AS pf ON psc.function_id = pf.function_id LEFT JOIN sys.filegroups AS fg ON si.data_space_id = fg.data_space_id /* Key list */ OUTER APPLY ( SELECT STUFF(( SELECT N', ' + QUOTENAME(c.name) + CASE ic.is_descending_key WHEN 1 THEN N' DESC' ELSE N'' END FROM sys.index_columns AS ic INNER JOIN sys.columns AS c ON ic.column_id = c.column_id AND ic.object_id = c.object_id WHERE ic.object_id = si.object_id AND ic.index_id = si.index_id AND ic.key_ordinal > 0 ORDER BY ic.key_ordinal FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'), 1, 2, '') ) AS keys(key_definition) /* Partitioning Ordinal */ OUTER APPLY ( SELECT MAX(QUOTENAME(c.name)) AS column_name FROM sys.index_columns AS ic INNER JOIN sys.columns AS c ON ic.column_id = c.column_id AND ic.object_id = c.object_id WHERE ic.object_id = si.object_id AND ic.index_id = si.index_id AND ic.partition_ordinal = 1 ) AS partitioning_column /* Include list */ OUTER APPLY ( SELECT STUFF(( SELECT N', ' + QUOTENAME(c.name) FROM sys.index_columns AS ic INNER JOIN sys.columns AS c ON ic.column_id = c.column_id AND ic.object_id = c.object_id WHERE ic.object_id = si.object_id AND ic.index_id = si.index_id AND ic.is_included_column = 1 ORDER BY c.name FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'), 1, 2, '') ) AS includes(include_definition) /* Partitions */ OUTER APPLY ( SELECT COUNT(*) AS partition_count, CAST(SUM(ps.in_row_reserved_page_count) * 8. / 1024. / 1024. AS NUMERIC(32, 1)) AS reserved_in_row_GB, CAST(SUM(ps.lob_reserved_page_count) * 8. / 1024. / 1024. AS NUMERIC(32, 1)) AS reserved_LOB_GB, SUM(ps.row_count) AS row_count FROM sys.partitions AS p INNER JOIN sys.dm_db_partition_stats AS ps ON p.partition_id = ps.partition_id WHERE p.object_id = si.object_id AND p.index_id = si.index_id ) AS partition_sums /* row compression list by partition */ OUTER APPLY ( SELECT STUFF(( SELECT N', ' + CAST(p.partition_number AS VARCHAR(32)) FROM sys.partitions AS p WHERE p.object_id = si.object_id AND p.index_id = si.index_id AND p.data_compression = 1 ORDER BY p.partition_number FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'), 1, 2, '') ) AS row_compression_clause(row_compression_partition_list) /* data compression list by partition */ OUTER APPLY ( SELECT STUFF(( SELECT N', ' + CAST(p.partition_number AS VARCHAR(32)) FROM sys.partitions AS p WHERE p.object_id = si.object_id AND p.index_id = si.index_id AND p.data_compression = 2 ORDER BY p.partition_number FOR XML PATH(''), TYPE ).value('.', 'NVARCHAR(MAX)'), 1, 2, '') ) AS page_compression_clause(page_compression_partition_list) WHERE si.type IN (0, 1, 2) /* heap, clustered, nonclustered */ AND si.index_id NOT IN (0, 1) ORDER BY table_name, si.index_id OPTION (RECOMPILE); GO
Results
Script results screen:
Script results detailed description:
Column name | Data type | Description |
---|---|---|
database_name | sysname | Name of actual database |
table_name | sysname | Table name with schema name where index is created in |
last_user_read | datetime | Time of last user seek, scan or lookup |
last_user_update | datetime | Time of last user update. |
checking_statement | nvarchar(MAX) | T-SQL statement that can be used when recreating this index. You can check if index already exists. |
index_create_statement | nvarchar(MAX) | T-SQL statement that can be used to create this index |
index_id | int | ID of the index. index_id is unique only within the object. 0 = Heap, 1 = Clustered index, > 1 = Nonclustered index. |
index_name | sysname | Name of the index. name is unique only within the object. NULL = Heap. |
reserved_in_row_GB | bigint | Total GB size of pages reserved for storing and managing in-row data in this partition, regardless of whether the pages are in use or not. Always 0 for a columnstore index. |
reserved_LOB_GB | bigint | Total GB size of pages reserved for storing and managing out-of-row text, ntext, image, varchar(max), nvarchar(max), varbinary(max), and xml columns within the partition, regardless of whether the pages are in use or not. IAM pages are included. Total number of LOBs reserved for storing and managing a columnstore index in the partition. |
row_count | bigint | The approximate number of rows in the partition. |
user_seeks | bigint | Number of seeks by user queries. |
user_scans | bigint | Number of scans by user queries that did not use 'seek' predicate. |
user_lookups | bigint | Number of bookmark lookups by user queries. |
user_updates | bigint | Number of updates by user queries. This includes Insert, Delete, and Updates representing number of operations done not the actual rows affected. For example, if you delete 1000 rows in one statement, this count increments by 1 |
partition_count | bigint | Number of partitions of index is partitioned. |
allow_page_locks | bit | 1 = Index allows page locks. 0 = Index does not allow page locks. Always 0 for clustered columnstore indexes. |
allow_row_locks | bit | 1 = Index allows row locks. 0 = Index does not allow row locks. Always 0 for clustered columnstore indexes. |
is_hypothetical | bit | 1 = Index is hypothetical and cannot be used directly as a data access path. Hypothetical indexes hold column-level statistics. 0 = Index is not hypothetical. |
has_filter | bit | 1 = Index has a filter and only contains rows that satisfy the filter definition. 0 = Index does not have a filter. |
fill_factor | tinyint | > 0 = FILLFACTOR percentage used when the index was created or rebuilt. 0 = Default value Always 0 for clustered columnstore indexes. |
is_unique | bit | 1 = Index is unique. 0 = Index is not unique. Always 0 for clustered columnstore indexes. |
partition_function | sysname | Name of the partition function. Is unique within the database. |
partition_scheme_or_filegroup | sysname | Name of data space, unique within the database. |
table_created_date | datetime | Date the table object was created. |
table_modified_date | datetime | Date the table object was last modified by using an ALTER statement. If the object is a table or a view, modify_date also changes when a clustered index on the table or view is created or altered. |