为索引列表生成CREATE脚本

时间:2009-07-13 19:18:58

标签: sql sql-server-2005

作为整理更改练习的一部分,我有一个索引列表(122)需要删除然后重新创建。如何重新创建这些索引而无需每次都通过GUI并将其编写到查询窗口?

我的索引列表是从这个脚本

获得的
WITH indexCTE AS
    (   
    SELECT Table_Name, Column_Name, Collation_Name 
    FROM information_schema.columns 
    WHERE Collation_Name IS NOT NULL AND Collation_Name = 'Modern_Spanish_CI_AS'
    ), 
    indexCTE2 AS
    (
    SELECT i.Name [Index Name], OBJECT_NAME(i.object_ID) [Table Name], c.Name [Column Name]
    FROM sys.indexes i 
    INNER JOIN sys.index_columns ic ON i.index_id = ic.index_id AND i.object_id = ic.object_id
    INNER JOIN sys.columns c ON ic.column_id = c.column_id AND ic.object_id = c.OBJECT_ID
    WHERE EXISTS (SELECT 1 FROM indexCTE t1 WHERE t1.Table_Name = OBJECT_NAME(i.object_ID) AND t1.Column_Name = c.Name)
    ) SELECT * FROM indexCTE2

你可能会说,我还是小DBA所以请耐心等待我!

谢谢!

5 个答案:

答案 0 :(得分:10)

你非常接近,我会说 - 我试过这个,你可以验证这是否适合你,并向你展示要重建的预期122个索引吗?

UPDATE :添加了确定CLUSTERED与NONCLUSTERED索引类型的功能,并将INCLUDEd列添加到索引定义中。

WITH indexCTE AS
(
    SELECT DISTINCT 
        i.index_id, i.name, i.object_id
    FROM 
        sys.indexes i 
    INNER JOIN
        sys.index_columns ic 
           ON i.index_id = ic.index_id AND i.object_id = ic.object_id
    WHERE 
        EXISTS (SELECT * FROM sys.columns c 
                 WHERE c.collation_name = 'Modern_Spanish_CI_AS' 
                 AND c.column_id = ic.column_id AND c.object_id = ic.object_id)
), 
indexCTE2 AS
(
    SELECT 
        indexCTE.name 'IndexName', 
        OBJECT_NAME(indexCTE.object_ID) 'TableName',
        CASE indexCTE.index_id 
          WHEN 1 THEN 'CLUSTERED'
          ELSE 'NONCLUSTERED'
        END AS 'IndexType', 
        (SELECT DISTINCT c.name + ','
         FROM 
            sys.columns c 
         INNER JOIN
            sys.index_columns ic 
               ON c.object_id = ic.object_id AND ic.column_id = c.column_id AND ic.Is_Included_Column = 0
         WHERE
            indexCTE.OBJECT_ID = ic.object_id 
            AND indexCTE.index_id = ic.index_id 
         FOR XML PATH('')
        ) ixcols,
        ISNULL(
        (SELECT DISTINCT c.name + ','
         FROM 
            sys.columns c 
         INNER JOIN
            sys.index_columns ic 
               ON c.object_id = ic.object_id AND ic.column_id = c.column_id AND ic.Is_Included_Column = 1
         WHERE
            indexCTE.OBJECT_ID = ic.object_id 
            AND indexCTE.index_id = ic.index_id 
         FOR XML PATH('')
        ), '') includedcols
    FROM 
        indexCTE
) 
SELECT 
    'CREATE ' + IndexType + ' INDEX ' + IndexName + ' ON ' + TableName + 
        '(' + SUBSTRING(ixcols, 1, LEN(ixcols)-1) + 
        CASE LEN(includedcols)
          WHEN 0 THEN ')'
          ELSE ') INCLUDE (' + SUBSTRING(includedcols, 1, LEN(includedcols)-1) + ')'
        END
FROM 
   indexCTE2
ORDER BY 
   TableName, IndexName

你得到了你正在寻找的CREATE INDEX陈述吗?

马克

答案 1 :(得分:4)

伟大的剧本Marc。 我认为唯一缺少的是每列上的升序或降序指标。我修改了你的脚本,包括一个case语句,用于根据sys.index_columns视图的is_descending_key列在ASC或DESC中添加索引列。

WITH indexCTE AS
(
    SELECT DISTINCT 
        i.index_id, i.name, i.object_id
    FROM 
        sys.indexes i 
    INNER JOIN
        sys.index_columns ic 
           ON i.index_id = ic.index_id AND i.object_id = ic.object_id
    WHERE 
        EXISTS (SELECT * FROM sys.columns c 
                 WHERE 
                 c.collation_name = 'Modern_Spanish_CI_AS' 
                 AND c.column_id = ic.column_id AND c.object_id = ic.object_id)
), 
indexCTE2 AS
(
    SELECT 
        indexCTE.name 'IndexName', 
        OBJECT_NAME(indexCTE.object_ID) 'TableName',
        CASE indexCTE.index_id 
          WHEN 1 THEN 'CLUSTERED'
          ELSE 'NONCLUSTERED'
        END AS 'IndexType', 
        (SELECT CASE WHEN ic.is_descending_key = 1 THEN c.name + ' DESC ,'
                ELSE c.name + ' ASC ,'
                END 
         FROM 
            sys.columns c 
         INNER JOIN
            sys.index_columns ic 
               ON c.object_id = ic.object_id AND ic.column_id = c.column_id AND ic.Is_Included_Column = 0
         WHERE
            indexCTE.OBJECT_ID = ic.object_id 
            AND indexCTE.index_id = ic.index_id 
         FOR XML PATH('')
        ) ixcols,
        ISNULL(
        (SELECT DISTINCT c.name + ','
         FROM 
            sys.columns c 
         INNER JOIN
            sys.index_columns ic 
               ON c.object_id = ic.object_id AND ic.column_id = c.column_id AND ic.Is_Included_Column = 1
         WHERE
            indexCTE.OBJECT_ID = ic.object_id 
            AND indexCTE.index_id = ic.index_id 
         FOR XML PATH('')
        ), '') includedcols
    FROM 
        indexCTE
) 
SELECT 
    'CREATE ' + IndexType + ' INDEX ' + IndexName + ' ON ' + TableName + 
        '(' + SUBSTRING(ixcols, 1, LEN(ixcols)-1) + 
        CASE LEN(includedcols)
          WHEN 0 THEN ')'
          ELSE ') INCLUDE (' + SUBSTRING(includedcols, 1, LEN(includedcols)-1) + ')'
        END
FROM 
   indexCTE2
ORDER BY 
   TableName, IndexName

答案 2 :(得分:1)

DECLARE @T_IndexInfo TABLE
    (
      IndID NVARCHAR(128),
      ObjectID NVARCHAR(128),
      ColID NVARCHAR(128),
      IndexName NVARCHAR(128),
      TableName NVARCHAR(128),
      ColumnName NVARCHAR(128),
      KeyNo NVARCHAR(128),
      ColType NVARCHAR(128)
    )

INSERT  INTO @T_IndexInfo
        SELECT  I.IndID,
                SO.ID AS 'ObjectID',
                SK.ColID,
                I.Name AS 'IndexName',
                SO.Name AS 'TableName',
                SC.Name AS 'ColumnName',
                Sk.KeyNo,
                CASE WHEN Sk.KeyNo = 0 THEN 'Include'
                     ELSE 'Normal'
                END AS 'ColType'
        FROM    sys.sysindexes I
                INNER JOIN sys.sysobjects SO ON SO.ID = I.ID
                                                AND SO.xtype = 'U'
                INNER JOIN sys.sysindexkeys SK ON SK.IndID = I.IndID
                                                  AND SO.ID = SK.ID
                INNER JOIN sys.syscolumns SC ON SC.ID = SO.ID
                                                AND SC.ColID = SK.ColID
        WHERE   I.IndID > 0
                AND I.IndID < 255
                AND ( I.Status & 64 ) = 0
--                AND ( I.status & 2048 ) <> 2048   /******** comment this if PK's also need to be recreated *****/
        ORDER BY SO.Name,
                I.Name

DECLARE @T_Final TABLE
    (
      TableName NVARCHAR(128),
      IndexName NVARCHAR(128),
      NormalColumns NVARCHAR(MAX),
      IncludedColumns NVARCHAR(MAX)
    )

INSERT  INTO @T_Final
        SELECT DISTINCT
                TableName,
                IndexName,
                STUFF(( SELECT  ',[' + ColumnName + ']'
                        FROM    @T_IndexInfo
                        WHERE   IndID = I.IndID
                                AND ObjectID = I.ObjectID
                                AND ColType = 'Normal'
                        ORDER BY KeyNo
                      FOR
                        XML PATH('')
                      ), 1, 1, '') AS 'NormalColumns',
                STUFF(( SELECT  ',[' + ColumnName + ']'
                        FROM    @T_IndexInfo
                        WHERE   IndID = I.IndID
                                AND ObjectID = I.ObjectID
                                AND ColType = 'Include'
                      FOR
                        XML PATH('')
                      ), 1, 1, '') AS 'IncludedColumns'
        FROM    @T_IndexInfo I;

WITH indexCTE AS
    (   
    SELECT Table_Name, Column_Name --, Collation_Name 
    FROM information_schema.columns 
    WHERE Collation_Name IS NOT NULL AND Collation_Name = 'Modern_Spanish_CI_AS'
    ), 
    indexCTE2 AS
    (
    SELECT i.Name [Index Name], OBJECT_NAME(i.object_ID) [Table Name], c.Name [Column Name]
    FROM sys.indexes i 
    INNER JOIN sys.index_columns ic ON i.index_id = ic.index_id AND i.object_id = ic.object_id
    INNER JOIN sys.columns c ON ic.column_id = c.column_id AND ic.object_id = c.OBJECT_ID
    WHERE EXISTS (SELECT 1 FROM indexCTE t1 WHERE t1.Table_Name = OBJECT_NAME(i.object_ID) AND t1.Column_Name = c.Name)
    )   

SELECT IndexName, TableName, NormalColumns, IncludedColumns
INTO #temp1
FROM @T_Final z INNER JOIN indexCTE2 x ON z.IndexName = x.[Index Name]

-- To generate CREATE INDEX SCRIPT
SELECT  'CREATE INDEX [' + IndexName + '] ON [' + TableName + '].('
        + NormalColumns + ')' + CASE WHEN IncludedColumns IS NULL THEN ''
                                     ELSE ' INCLUDE (' + IncludedColumns + ')'
                                END AS 'CreateScript'
FROM    #temp1

-- To generate DROP INDEX SCRIPT
SELECT  'DROP INDEX [' + TableName + '].[' + IndexName + ']' AS 'DropScript'
FROM    #temp1

DROP TABLE#temp1

答案 3 :(得分:0)

这有点偏离主题,但我想我会建议这样做:

如果您不想继续在sql server management studio中执行脚本,可以创建一个runmyscripts.bat文件,其中包括:

@echo off

echo Execute Scripts...

sqlcmd -i C:\Scripts\myscript1.sql
sqlcmd -i C:\Scripts\myscript2.sql

echo Scripts Complete.
echo Press any button to exit.
pause

答案 4 :(得分:0)

TechNet有一个相对完整的解决方案。

根据您的需求调整查询:

  • sys.tables to sys.views
  • 删除select
  • 中的默认值
  • 删除/添加一些条件