【问题标题】:Disable all non-clustered indexes禁用所有非聚集索引
【发布时间】:2011-01-31 20:47:25
【问题描述】:

我从我的数据库中选择了一些非聚集索引,如下所示:

SELECT  sys.objects.name tableName,
        sys.indexes.name indexName
FROM    sys.indexes
        JOIN sys.objects ON sys.indexes.object_id = sys.objects.object_id
WHERE   sys.indexes.type_desc = 'NONCLUSTERED'
        AND sys.objects.type_desc = 'USER_TABLE'

我想对每个结果运行以下命令:

ALTER INDEX indexName ON tableName DISABLE

我该怎么做呢?有没有更好的办法?

编辑

我这样做是为了截断表,然后使用“ALTER INDEX bla ON table REBUILD”进行重建。这需要自动化,因此删除和重建将是我宁愿避免的更高级别的维护活动。这是一个糟糕的计划吗?我需要一种以最小开销清空表格的方法。

【问题讨论】:

  • 您最好还是放弃它们,因为没有 ALTER INDEX ENABLE,它们需要重新创建。如果禁用,它们将不会被维护。
  • 如果删除,您会丢失定义,并且如果(何时)需要重新创建它们,则必须记住它们。如果禁用,定义将保留在系统表中,您不必担心从头开始重新创建它们。
  • @jl 您可以使用 ALTER INDEX REBUILD 重新启用索引。

标签: sql-server tsql sql-server-2008 indexing


【解决方案1】:

您可以将查询构建到 select 语句中,如下所示:

DECLARE @sql AS VARCHAR(MAX)='';

SELECT @sql = @sql + 
'ALTER INDEX ' + sys.indexes.name + ' ON  ' + sys.objects.name + ' DISABLE;' +CHAR(13)+CHAR(10)
FROM 
    sys.indexes
JOIN 
    sys.objects 
    ON sys.indexes.object_id = sys.objects.object_id
WHERE sys.indexes.type_desc = 'NONCLUSTERED'
  AND sys.objects.type_desc = 'USER_TABLE';

EXEC(@sql);

字符 13 和 10 是换行符/回车符,因此您可以通过将 EXEC 替换为 PRINT 来检查输出,它会更具可读性。

【讨论】:

  • 但是把它放在某种形式的循环中(游标或临时表/WHILE构造),依次处理每个索引
  • 好点,但禁用非聚集索引的成本太高以至于有必要吗?
  • 这只会拉取集合中的最后一个结果。声明应该是 DECLARE @sql AS VARCHAR(MAX)='';并且选择应该开始 SELECT @sql = @sql+'ALTE... 将所有结果连接在一起。否则,非常好。我现在要试试这个。
  • 这个脚本很棒。它可能是不同版本的 SQL Server。但是indexNametableName 对我不起作用:Invalid column name 'indexName'。所以我使用了sys.indexes.namesys.objects.name。我正在使用 SQL 2012。
  • 感谢@TarasB,我在 2010 年写了这篇文章,当时我正在使用 MS-SQL 2005 进行项目。我面前没有 2012,但你可能想确保@987654329 @ 是唯一的,并且连接将起作用。也许object_id 已重命名为id 或类似名称。
【解决方案2】:

使用索引和表名构建一个表变量。使用循环遍历它们,并为它们中的每一个执行动态 SQL 语句。

declare @Indexes table
(
    Num       int identity(1,1) primary key clustered,
    TableName nvarchar(255),
    IndexName nvarchar(255)
)

INSERT INTO @Indexes
(
    TableName,
    IndexName
)
SELECT  sys.objects.name tableName,
        sys.indexes.name indexName
FROM    sys.indexes
        JOIN sys.objects ON sys.indexes.object_id = sys.objects.object_id
WHERE   sys.indexes.type_desc = 'NONCLUSTERED'
        AND sys.objects.type_desc = 'USER_TABLE'

DECLARE @Max INT
SET @Max = @@ROWCOUNT

SELECT @Max as 'max'
SELECT * FROM @Indexes

DECLARE @I INT
SET @I = 1

DECLARE @TblName NVARCHAR(255), @IdxName NVARCHAR(255)

DECLARE @SQL NVARCHAR(MAX)

WHILE @I <= @Max
BEGIN
    SELECT @TblName = TableName, @IdxName = IndexName FROM @Indexes WHERE Num = @I
    SELECT @SQL = N'ALTER INDEX ' + @IdxName + N' ON ' + @TblName + ' DISABLE;'

    EXEC sp_sqlexec @SQL    

    SET @I = @I + 1

END

【讨论】:

    【解决方案3】:

    OTOH,DROP 而不是 DISABLE 可能会更好(或者它是 Oracle 和 MS SQL 之间的微小语法差异?:-) 我提到的原因是我记得每天重新填充和 excplicilty 非规范化两次的表,并且我们正在 DROP-ing 所有索引,以强制 DB 在加载新日期并重建所有索引后重建索引和 sproc 执行计划。

    当然,我们为此有单独的脚本,因为一旦删除它们,索引就不再存在于系统表中。

    【讨论】:

    • 禁用非聚集索引会保留定义,并允许重新创建/重建大约 50% 的删除和创建速度。
    【解决方案4】:

    使用光标来编写脚本比临时表更惯用(而且稍微简洁一些)。要重新启用索引,请将 DISABLE 替换为 REBUILD。

    DECLARE cur_indexes CURSOR FOR
    SELECT  sys.objects.name tableName,
            sys.indexes.name indexName
    FROM    sys.indexes
            JOIN sys.objects ON sys.indexes.object_id = sys.objects.object_id
    WHERE   sys.indexes.type_desc = 'NONCLUSTERED'
            AND sys.objects.type_desc = 'USER_TABLE'
    
    DECLARE @TblName NVARCHAR(255), @IdxName NVARCHAR(255)
    
    DECLARE @SQL NVARCHAR(MAX)
    
    open cur_indexes
    fetch next from cur_indexes into @TblName, @IdxName
    
    WHILE @@FETCH_STATUS = 0
    BEGIN
        SELECT @SQL = N'ALTER INDEX ' + @IdxName + N' ON ' + @TblName + ' DISABLE;'
    
        EXEC sp_sqlexec @SQL    
    
        fetch next from cur_indexes into @TblName, @IdxName
    END
    
    close cur_indexes
    deallocate cur_indexes
    

    【讨论】:

    • 我相信您在第 1 行中缺少 CURSOR 关键字。 DECLARE cur_indexes CURSOR FOR
    【解决方案5】:

    只禁用唯一的非聚集索引

    DECLARE @EnableOrRebuild as nvarchar(20)
    SET @EnableOrRebuild = 'DISABLE'
    /* SET @EnableOrRebuild = 'REBUILD' */ -- Uncomment for REBUILD
    DECLARE @TableName as nvarchar(200) = 'ChorusDestinataire' -- Enter your table name here
    DECLARE @SchemaName as nvarchar(200) = 'dbo' -- Enter the schema here
    DECLARE @Sql as nvarchar(2000)=''
    
    SELECT @Sql = @Sql + N'ALTER INDEX ' + quotename(i.name) + N' ON ' + quotename(s.name) + '.' + quotename(o.name) + ' ' + @EnableOrRebuild + N';' + CHAR(13) + CHAR(10)
      FROM sys.indexes i
     INNER JOIN sys.objects o ON i.object_id = o.object_id
     INNER JOIN sys.schemas s ON s.schema_id = o.schema_id
     WHERE i.type_desc = N'NONCLUSTERED'
       AND i.ignore_dup_key = 1
       AND o.type_desc = N'USER_TABLE'
       AND o.name = @TableName 
       AND s.name = @SchemaName
    
     -- SELECT @Sql;
    EXEC (@Sql);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-07
      • 2020-08-04
      • 2010-09-10
      • 2017-04-01
      • 2021-01-14
      • 1970-01-01
      • 2016-01-05
      相关资源
      最近更新 更多