【问题标题】:Generate CREATE scripts for a list of indexes为索引列表生成 CREATE 脚本
【发布时间】:2009-07-13 19:18:58
【问题描述】:

作为排序规则更改练习的一部分,我有一个需要删除然后重新创建的索引列表 (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

如您所知,我仍然是 Jr. DBA,所以请耐心等待!

谢谢!

【问题讨论】:

    标签: sql sql-server-2005


    【解决方案1】:

    你已经很接近了,我会说 - 我试过了,你能验证它是否适合你,并向你显示预期的 122 个要重新创建的索引吗??

    更新:添加了确定 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 语句了吗??

    马克

    【讨论】:

    • 这个问题是我有一些索引是复合索引。其中一些还包含列或独特等。我不能像那样重新创建它们。我需要为每个索引复制原始架构...
    • 有什么问题?该脚本应读取 ALL 索引列 - 无论有多少列。
    • 是的,我有列名,但没有属性。例如,索引可能是非聚集的并且包含列,我需要指定 CREATE NONCLUSTERED INDEX ON () INCLUDE 等。我无法获取包含的列和这样的。
    • 好的,这也可以扩展到处理这些情况——我认为这不是什么大问题。
    • 更新为包含索引类型(非聚集与聚集)和“包含”列
    【解决方案2】:

    伟大的剧本马克。 我认为它唯一缺少的是每列上的升序或降序指示符。我已经修改了您的脚本,以便根据 sys.index_columns 视图的 is_descending_key 列在 ASC 或 DESC 中添加索引列的 case 语句。

    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
    

    【讨论】:

      【解决方案3】:
      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
      

      删除表 #temp1

      【讨论】:

        【解决方案4】:

        这有点离题,但我想我还是会建议这个:

        如果您不想在 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
        

        【讨论】:

          【解决方案5】:

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

          根据您的需要调整查询:

          • sys.tables 到 sys.views
          • 删除选择中的默认值
          • 删除/添加一些 where 条件

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2010-09-23
            • 1970-01-01
            相关资源
            最近更新 更多