Technical Article

usp_IndexCreationStatements

,

(SQL 2005) Creates Index Creation statements based on info colected in system view sys.dm_db_missing_index_details, either with included columns or just In(Equality) columns.

EDIT: Statements are shown in results pane because of need to properly indent the output code.

EDIT2: Added check for redundant indexes, so dba can opt out of creating them.

EDIT3: Wrong drop statements fixed.

IF EXISTS (SELECT name FROM dbo.sysobjects WHERE id = Object_id(N'[dbo].[usp_IndexCreation]') AND OBJECTPROPERTY(id,N'IsProcedure') = 1)
DROP PROCEDURE [dbo].[usp_IndexCreation]
GO

SET QUOTED_IDENTIFIER ON 
GO
SET ANSI_NULLS ON 
GO

CREATE PROC usp_IndexCreation AS
--
-- usp_IndexCreation.sql - Index creation statements
-- SQL2005/2008 only
-- 2007-10-11 Pedro Lopes (NovaBase) pedro.lopes@novabase.pt
--
-- 2008-01-17 Verify redundant indexes before creation.
-- EXEC usp_IndexCreation
--
SET NOCOUNT ON

DECLARE @IC VARCHAR(4000), @ICWI VARCHAR(4000), @RI VARCHAR(4000), @DB VARCHAR(100), @IHK int, @TBL VARCHAR(100), @Qryins VARCHAR(4000)

CREATE TABLE #IndexCreation (
    [DBid] int,
    DBName VARCHAR(100),
    [Table] VARCHAR(100),
    [Equality] VARCHAR(1000),
    [Included] VARCHAR(4000),
    [Ix_Handle_Key] int,
    Col1 VARCHAR(100) NULL,
    Col2 VARCHAR(100) NULL,
    Col3 VARCHAR(100) NULL,
    Col4 VARCHAR(100) NULL,
    Col5 VARCHAR(100) NULL,
    Col6 VARCHAR(100) NULL,
    Col7 VARCHAR(100) NULL,
    Col8 VARCHAR(100) NULL,
    Col9 VARCHAR(100) NULL,
    Col10 VARCHAR(100) NULL,
    Col11 VARCHAR(100) NULL,
    Col12 VARCHAR(100) NULL,
    Col13 VARCHAR(100) NULL,
    Col14 VARCHAR(100) NULL,
    Col15 VARCHAR(100) NULL,
    Col16 VARCHAR(100) NULL
    )

CREATE TABLE #IndexCreationSec (
    [Ix_Handle_Key] int,
    DBName VARCHAR(100),
    [Table] VARCHAR(100),
    [Equality] VARCHAR(1000),
    )

CREATE TABLE #TempIndexCreation(
    DBName sysname,
    Ix_Handle_Key int,
    [Table] VARCHAR(50),
    Col1 VARCHAR(100) NULL,
    Col2 VARCHAR(100) NULL,
    Col3 VARCHAR(100) NULL,
    Col4 VARCHAR(100) NULL,
    Col5 VARCHAR(100) NULL,
    Col6 VARCHAR(100) NULL,
    Col7 VARCHAR(100) NULL,
    Col8 VARCHAR(100) NULL,
    Col9 VARCHAR(100) NULL,
    Col10 VARCHAR(100) NULL,
    Col11 VARCHAR(100) NULL,
    Col12 VARCHAR(100) NULL,
    Col13 VARCHAR(100) NULL,
    Col14 VARCHAR(100) NULL,
    Col15 VARCHAR(100) NULL,
    Col16 VARCHAR(100) NULL
    )

INSERT INTO #IndexCreation ([DBid],DBName,[Table],[Equality],[Included],[Ix_Handle_Key])
SELECT i.database_id AS [DBid], 
    m.[name] AS DBName, 
    i.[statement] AS [Table], 
    CASE    WHEN (i.equality_columns IS NOT NULL AND i.inequality_columns IS NULL) THEN i.equality_columns
            WHEN (i.equality_columns IS NULL AND i.inequality_columns IS NOT NULL) THEN i.inequality_columns
            ELSE i.equality_columns + ', ' + i.inequality_columns END AS [Equality], 
    i.included_columns AS [Included],
    i.index_handle [Ix_Handle_Key]
FROM sys.dm_db_missing_index_details i, master..sysdatabases m
WHERE i.database_id = m.dbid
ORDER BY database_id, Equality, i.index_handle

INSERT INTO #IndexCreationSec
SELECT MAX(DISTINCT [Ix_Handle_Key]) AS [Ix_Handle_Key], DBName, [Table], [Equality] FROM #IndexCreation 
GROUP BY DBName, [Table], [Equality]

SELECT DBName, COUNT(DISTINCT [Ix_Handle_Key]) AS Indexes_to_Create FROM #IndexCreationSec
GROUP BY DBName

SELECT DBName, COUNT(DISTINCT [Ix_Handle_Key]) AS Indexes_with_INCLUDEs_to_Create FROM #IndexCreation
WHERE [Included] IS NOT NULL
GROUP BY DBName

PRINT '############# Index creation statements #############' + CHAR(10)

DECLARE cIC CURSOR FOR
SELECT 'USE ' + QUOTENAME(DBName) + CHAR(10) + 'GO' + CHAR(10) + 'IF EXISTS (SELECT name FROM sys.indexes WHERE name = N''IX_' + 
    CAST([Ix_Handle_Key] AS NVARCHAR) + ''') DROP INDEX ' + [Table] + '.IX_' + 
    CAST([Ix_Handle_Key] AS NVARCHAR) + CHAR(10) + 'GO' + CHAR(10) + 'CREATE NONCLUSTERED INDEX IX_' + 
    CAST([Ix_Handle_Key] AS NVARCHAR) + ' ON ' + [Table] + ' (' + [Equality] + ') WITH PAD_INDEX, FILLFACTOR = 90' + CHAR(10) + 'GO' + CHAR(10) -- AS CreateStatement 
FROM #IndexCreationSec
ORDER BY DBName, [Table], [Ix_Handle_Key]
OPEN cIC 
FETCH NEXT FROM cIC INTO @IC
WHILE @@FETCH_STATUS = 0 
    BEGIN 
        PRINT @IC
        FETCH NEXT FROM cIC INTO @IC
    END
CLOSE cIC 
DEALLOCATE cIC

PRINT '############# Index creation statements with INCLUDEs #############' + CHAR(10)

DECLARE cICWI CURSOR FOR
SELECT 'USE ' + QUOTENAME(DBName) + CHAR(10) + 'GO' + CHAR(10) + 'IF EXISTS (SELECT name FROM sys.indexes WHERE name = N''IX_' + 
    CAST([Ix_Handle_Key] AS NVARCHAR) + ''') DROP INDEX ' + [Table] + '.IX_' + 
    CAST([Ix_Handle_Key] AS NVARCHAR) + CHAR(10) + 'GO' + CHAR(10) + 'CREATE NONCLUSTERED INDEX IX_' + 
    CAST([Ix_Handle_Key] AS NVARCHAR) + ' ON ' + [Table] + ' (' + [Equality] + ')' + CHAR(10) + 'INCLUDE (' + [Included] + ') WITH PAD_INDEX, FILLFACTOR = 90' + CHAR(10) + 'GO' + CHAR(10) -- AS CreateStatementWithInclude 
FROM #IndexCreation
WHERE [Included] IS NOT NULL
ORDER BY DBName, [Ix_Handle_Key]
OPEN cICWI 
FETCH NEXT FROM cICWI INTO @ICWI
WHILE @@FETCH_STATUS = 0 
    BEGIN 
        PRINT @ICWI
        FETCH NEXT FROM cICWI INTO @ICWI
    END
CLOSE cICWI 
DEALLOCATE cICWI


DECLARE cRI CURSOR FOR SELECT QUOTENAME(DBName), [Ix_Handle_Key], [Table], [Equality]
                        FROM #IndexCreation
                        --WHERE [Included] IS NOT NULL
                        GROUP BY DBName, [Table], [Equality], [Ix_Handle_Key]
                        ORDER BY DBName, [Ix_Handle_Key]
OPEN cRI 
FETCH NEXT FROM cRI INTO @DB, @IHK, @TBL, @RI
WHILE @@FETCH_STATUS = 0 
    BEGIN 
        DECLARE @tblCol VARCHAR(4000)
        DECLARE @Col VARCHAR(50), @Colctr int, @Colctrdesc int, @StartPos int, @Length int
        SET @Colctr = 0
        SET @Colctrdesc = 0
        WHILE LEN(@RI) > 0
            BEGIN
                SET @StartPos = CHARINDEX(',', @RI)
                IF @StartPos < 0 SET @StartPos = 0
                SET @Length = LEN(@RI) - @StartPos - 1
                IF @Length < 0 SET @Length = 0
                IF @StartPos > 0
                    BEGIN
                        SET @Col = SUBSTRING(@RI, 1, @StartPos - 1)
                        SET @RI = SUBSTRING(@RI, @StartPos + 1, LEN(@RI) - @StartPos)
                        SET @Colctr = @Colctr + 1
                    END
                ELSE
                    BEGIN
                        SET @Col = @RI
                        SET @RI = ''
                        SET @Colctr = @Colctr + 1
                    END
                IF @tblCol IS NULL
                    BEGIN SET @tblCol = CHAR(39) + LTRIM(RTRIM(@Col)) + CHAR(39) END
                ELSE
                    BEGIN SET @tblCol = @tblCol + ',' + CHAR(39) + LTRIM(RTRIM(@Col)) + CHAR(39) END
            END
        IF @Colctr < 16
            BEGIN
                SET @Colctrdesc = 16 - @Colctr
                WHILE @Colctrdesc > 0
                    BEGIN
                        SET @tblCol = @tblCol + ',' + CHAR(39) + CHAR(39)
                        SET @Colctrdesc = @Colctrdesc - 1
                    END
                SET @Colctr = 0
            END
        SET @Qryins = 'INSERT INTO #TempIndexCreation (DBName,Ix_Handle_Key,[Table],Col1,Col2,Col3,Col4,Col5,Col6,Col7,Col8,Col9,Col10,Col11,Col12,Col13,Col14,Col15,Col16) VALUES (''' + @DB + ''',' + RTRIM(@IHK) + ',''' + @TBL + ''',' + @tblCol + ')'
        EXECUTE (@Qryins)
        SET @tblCol = NULL
        FETCH NEXT FROM cRI INTO @DB, @IHK, @TBL, @RI
    END
CLOSE cRI 
DEALLOCATE cRI

UPDATE #IndexCreation
SET #IndexCreation.Col1 = #TempIndexCreation.Col1,#IndexCreation.Col2 = #TempIndexCreation.Col2,#IndexCreation.Col3 = #TempIndexCreation.Col3,#IndexCreation.Col4 = #TempIndexCreation.Col4,#IndexCreation.Col5 = #TempIndexCreation.Col5,
    #IndexCreation.Col6 = #TempIndexCreation.Col6,#IndexCreation.Col7 = #TempIndexCreation.Col7,#IndexCreation.Col8 = #TempIndexCreation.Col8,#IndexCreation.Col9 = #TempIndexCreation.Col9,#IndexCreation.Col10 = #TempIndexCreation.Col10,
    #IndexCreation.Col11 = #TempIndexCreation.Col11,#IndexCreation.Col12 = #TempIndexCreation.Col12,#IndexCreation.Col13 = #TempIndexCreation.Col13,#IndexCreation.Col14 = #TempIndexCreation.Col14,#IndexCreation.Col15 = #TempIndexCreation.Col15,
  #IndexCreation.Col16 = #TempIndexCreation.Col16
FROM #IndexCreation, #TempIndexCreation
WHERE #IndexCreation.[Table] = #TempIndexCreation.[Table] AND #IndexCreation.Ix_Handle_Key = #TempIndexCreation.Ix_Handle_Key

SELECT 'Possible Redundant Indexes (no INCLUDEs)' AS Comments, I.DBName, I.[Table], I.[Ix_Handle_Key] AS IndexID, 'IX_' + CAST(I.[Ix_Handle_Key] AS NVARCHAR) AS IndexName, I.[Equality] AS AllColName
FROM #IndexCreation I JOIN #IndexCreation I2
ON I.[Table] = I2.[Table] AND I.[Ix_Handle_Key] <> I2.[Ix_Handle_Key] AND I.Col1 = I2.Col2 AND I.Col2 = I2.Col1
ORDER BY I.[Table],I.[Ix_Handle_Key]

DROP TABLE #IndexCreation
DROP TABLE #IndexCreationSec
DROP TABLE #TempIndexCreation
GO

SET QUOTED_IDENTIFIER OFF
GO
SET ANSI_NULLS ON
GO

Rate

You rated this post out of 5. Change rating

Share

Share

Rate

You rated this post out of 5. Change rating