List out and script all indexes in a database
Its quite common that when there is some upgrade, change or release in application, there are chances that our database objects get modified. It may drop some indexes too hampering performance. I had similar situation in my profession. I had creating multiple indexes to find tune database performance, but then there was an application patch release. All those indexes we manually created were dropped by the application. Soon, there was blocking, deadlocks, long running queries, high cpu and performance became pathetic. Later it was understood that all those custom index we DBAs manually created were mercilessly removed by the application. 😥
After this incident, for every application patch release or change, we made a habit of keeping track of all the indexes in the database. We use below script to pull out the report before and after the release activity. The report would help us review, compare and recreate dropped indexes. The output the script is in such a way that you could save all the create statements in a text file and you could paste the list of indexes into an excel sheet for reporting.
/*
Adapted from http://www.sqlservercentral.com/Forums/Topic401784-562-2.aspx (by Jonathan AC Roberts. )
Modifications 10/06/2010 By R.Doering - http://sqlsolace.blogspot.com
1) Changed Schema of routine to Utils
2) Changed Name from INFGenerateIndexesScript to GenerateIndexesScript
3) Added Schemas to script
4) Reformatted for clarity
Modifications 2012-May-04 R. Gosling
1) Added in the Schema name to table name
Modifications 2015-Mar-12 Sherbaz.com
1) Removed the script out of Stored procedure
2) Outputs the index list as table which could be copied to Excel for reporting and comparing.
-- Usage: set variables @IncludeFileGroup, @IncludeDrop, @IncludeFillFactor and execute the whole script
*/
declare @IncludeFileGroup bit = 1,
@IncludeDrop bit = 1,
@IncludeFillFactor bit = 1
set nocount on
declare @indexType varchar(30), @indexColumns varchar(300), @indexIncludedColumns varchar(300), @script varchar(max)
if exists (select top 1 * from tempdb.sys.all_objects where name like '#indexlist{88dc7f9385b2c8971f38421926506de3e3fc5e843c9520f0674d4b7de6ae62da}')
drop table #indexlist
create table #indexlist
(
schemaName sysname,
tableName sysname,
tableId int,
indexName sysname,
indexId int,
[fillFactor] int,
columnNames varchar(300),
includedColumnNames varchar(300),
indexType varchar(100),
script varchar(max)
)
-- Get all existing indexes, but NOT the primary keys
DECLARE Indexes_cursor CURSOR
FOR SELECT SC.Name AS SchemaName,
SO.Name AS TableName,
SI.OBJECT_ID AS TableId,
SI.[Name] AS IndexName,
SI.Index_ID AS IndexId,
FG.[Name] AS FileGroupName,
CASE WHEN SI.Fill_Factor = 0 THEN 100 ELSE SI.Fill_Factor END Fill_Factor
FROM sys.indexes SI
LEFT JOIN sys.filegroups FG
ON SI.data_space_id = FG.data_space_id
INNER JOIN sys.objects SO
ON SI.OBJECT_ID = SO.OBJECT_ID
INNER JOIN sys.schemas SC
ON SC.schema_id = SO.schema_id
WHERE OBJECTPROPERTY(SI.OBJECT_ID, 'IsUserTable') = 1
AND SI.[Name] IS NOT NULL
AND SI.is_primary_key = 0
AND SI.is_unique_constraint = 0
AND INDEXPROPERTY(SI.OBJECT_ID, SI.[Name], 'IsStatistics') = 0
ORDER BY OBJECT_NAME(SI.OBJECT_ID), SI.Index_ID
DECLARE @SchemaName sysname
DECLARE @TableName sysname
DECLARE @TableId int
DECLARE @IndexName sysname
DECLARE @FileGroupName sysname
DECLARE @IndexId int
DECLARE @FillFactor int
DECLARE @NewLine nvarchar(4000)
SET @NewLine = char(13) + char(10)
DECLARE @Tab nvarchar(4000)
SET @Tab = SPACE(4)
-- Loop through all indexes
OPEN Indexes_cursor
FETCH NEXT
FROM Indexes_cursor
INTO @SchemaName, @TableName, @TableId, @IndexName, @IndexId, @FileGroupName, @FillFactor
WHILE (@@FETCH_STATUS = 0)
BEGIN
insert into #indexlist(schemaName, tableName, tableId, indexName, indexId, [fillFactor])
values(@SchemaName, @TableName, @TableId, @IndexName, @IndexId, @FillFactor)
DECLARE @sIndexDesc nvarchar(4000)
DECLARE @sCreateSql nvarchar(4000)
DECLARE @sDropSql nvarchar(4000)
SET @sIndexDesc = '-- Index ' + @IndexName + ' on table ' + @TableName
SET @sDropSql = 'IF EXISTS(SELECT 1' + @NewLine
+ ' FROM sysindexes si' + @NewLine
+ ' INNER JOIN sysobjects so' + @NewLine
+ ' ON so.id = si.id' + @NewLine
+ ' WHERE si.[Name] = N''' + @IndexName + ''' -- Index Name' + @NewLine
+ ' AND so.[Name] = N''' + @TableName + ''') -- Table Name' + @NewLine
+ 'BEGIN' + @NewLine
+ ' DROP INDEX [' + @IndexName + '] ON [' + @SchemaName + '].[' + @TableName + ']' + @NewLine
+ 'END' + @NewLine
SET @sCreateSql = 'CREATE '
SET @script = '';
set @script = 'CREATE '
SET @indexType = ''
-- Check if the index is unique
IF (IndexProperty(@TableId, @IndexName, 'IsUnique') = 1)
BEGIN
SET @sCreateSql = @sCreateSql + 'UNIQUE '
set @indexType = 'UNIQUE '
set @script = @script + 'UNIQUE '
END
--END IF
-- Check if the index is clustered
IF (IndexProperty(@TableId, @IndexName, 'IsClustered') = 1)
BEGIN
SET @sCreateSql = @sCreateSql + 'CLUSTERED '
SET @indexType = @indexType + 'CLUSTERED '
set @script = @script + 'CLUSTERED '
END
--END IF
update #indexlist set indexType = @indexType where tableId=@TableId and indexId=@IndexId
SET @sCreateSql = @sCreateSql + 'INDEX [' + @IndexName + '] ON [' + @SchemaName + '].[' + @TableName + ']' + @NewLine + '(' + @NewLine
SET @script = @script + 'INDEX [' + @IndexName + '] ON [' + @SchemaName + '].[' + @TableName + ']('
-- Get all columns of the index
DECLARE IndexColumns_cursor CURSOR
FOR SELECT SC.[Name],
IC.[is_included_column],
IC.is_descending_key
FROM sys.index_columns IC
INNER JOIN sys.columns SC
ON IC.OBJECT_ID = SC.OBJECT_ID
AND IC.Column_ID = SC.Column_ID
WHERE IC.OBJECT_ID = @TableId
AND Index_ID = @IndexId
ORDER BY IC.[is_included_column],
IC.key_ordinal
DECLARE @IxColumn sysname
DECLARE @IxIncl bit
DECLARE @Desc bit
DECLARE @IxIsIncl bit
SET @IxIsIncl = 0
DECLARE @IxFirstColumn bit
SET @IxFirstColumn = 1
set @indexColumns = ''
set @indexIncludedColumns = ''
-- Loop through all columns of the index and append them to the CREATE statement
OPEN IndexColumns_cursor
FETCH NEXT
FROM IndexColumns_cursor
INTO @IxColumn, @IxIncl, @Desc
WHILE (@@FETCH_STATUS = 0)
BEGIN
IF (@IxFirstColumn = 1)
BEGIN
SET @IxFirstColumn = 0
END
ELSE
BEGIN
--check to see if it's an included column
IF (@IxIsIncl = 0) AND (@IxIncl = 1)
BEGIN
SET @IxIsIncl = 1
SET @sCreateSql = @sCreateSql + @NewLine + ')' + @NewLine + 'INCLUDE' + @NewLine + '(' + @NewLine
set @indexIncludedColumns = @indexIncludedColumns + ' - ' + @IxColumn
SET @script = @script + ')' + ' INCLUDE ('
END
ELSE
BEGIN
SET @sCreateSql = @sCreateSql + ',' + @NewLine
set @indexColumns = @indexColumns + ' - ' + @IxColumn
SET @script = @script + ', '
END
--END IF
END
--END IF
SET @sCreateSql = @sCreateSql + @Tab + '[' + @IxColumn + ']'
if @indexColumns = ''
set @indexColumns = @indexColumns + ' - ' + @IxColumn
SET @script = @script + @Tab + '[' + @IxColumn + ']'
-- check if ASC or DESC
IF @IxIsIncl = 0
BEGIN
IF @Desc = 1
BEGIN
SET @sCreateSql = @sCreateSql + ' DESC'
set @indexColumns = @indexColumns + ':DESC'
SET @script = @script + ' DESC'
END
ELSE
BEGIN
SET @sCreateSql = @sCreateSql + ' ASC'
set @indexColumns = @indexColumns + ':ASC'
SET @script = @script + ' ASC'
END
--END IF
END
--END IF
update #indexlist set columnNames = @indexColumns, includedColumnNames = @indexIncludedColumns
where tableId = @TableId and indexId = @IndexId
FETCH NEXT
FROM IndexColumns_cursor
INTO @IxColumn, @IxIncl, @Desc
END
--END WHILE
CLOSE IndexColumns_cursor
DEALLOCATE IndexColumns_cursor
SET @sCreateSql = @sCreateSql + @NewLine + ') '
SET @script = @script + ') '
IF @IncludeFillFactor = 1
BEGIN
SET @sCreateSql = @sCreateSql + @NewLine + 'WITH (FillFactor = ' + CAST(@FillFactor AS varchar(13)) + ')' + @NewLine
SET @script = @script + ' WITH (FillFactor = ' + CAST(@FillFactor AS varchar(13)) + ')'
END
--END IF
IF @IncludeFileGroup = 1
BEGIN
SET @sCreateSql = @sCreateSql + 'ON ['+ @FileGroupName + ']' + @NewLine
SET @script = @script + 'ON ['+ @FileGroupName + ']'
END
ELSE
BEGIN
SET @sCreateSql = @sCreateSql + @NewLine
SET @script = @script
END
--END IF
PRINT '-- **********************************************************************'
PRINT @sIndexDesc
PRINT '-- **********************************************************************'
IF @IncludeDrop = 1
BEGIN
PRINT @sDropSql
PRINT 'GO'
END
--END IF
PRINT @sCreateSql
set @script = @script
update #indexlist set script = @script where tableId = @TableId and indexId = @IndexId
PRINT 'GO' + @NewLine + @NewLine
FETCH NEXT
FROM Indexes_cursor
INTO @SchemaName, @TableName, @TableId, @IndexName, @IndexId, @FileGroupName, @FillFactor
END
--END WHILE
CLOSE Indexes_cursor
DEALLOCATE Indexes_cursor
select * from #indexlist
set nocount off