根据碎片结果自动进行INDEX重建?

时间:2012-05-08 08:45:26

标签: sql sql-server sql-server-2005

是否可以添加维护作业来检查索引碎片。如果大于50%,那么自动重建这些索引?

索引大小可以从100MB到10GB不等。 SQL 2005。

谢谢。

4 个答案:

答案 0 :(得分:23)

我使用这个脚本。请注意我会建议你阅读我在这里使用的dmv它们是SQL2005 +中隐藏的宝石。

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED
CREATE TABLE #FragmentedIndexes
(
 DatabaseName SYSNAME
 , SchemaName SYSNAME
 , TableName SYSNAME
 , IndexName SYSNAME
 , [Fragmentation%] FLOAT
)

INSERT INTO #FragmentedIndexes
SELECT
 DB_NAME(DB_ID()) AS DatabaseName
 , ss.name AS SchemaName
 , OBJECT_NAME (s.object_id) AS TableName
 , i.name AS IndexName
 , s.avg_fragmentation_in_percent AS [Fragmentation%]
FROM sys.dm_db_index_physical_stats(db_id(),NULL, NULL, NULL, 'SAMPLED') s
INNER JOIN sys.indexes i ON s.[object_id] = i.[object_id]
AND s.index_id = i.index_id
INNER JOIN sys.objects o ON s.object_id = o.object_id
INNER JOIN sys.schemas ss ON ss.[schema_id] = o.[schema_id]
WHERE s.database_id = DB_ID()
AND i.index_id != 0
AND s.record_count > 0
AND o.is_ms_shipped = 0
DECLARE @RebuildIndexesSQL NVARCHAR(MAX)
SET @RebuildIndexesSQL = ''
SELECT
 @RebuildIndexesSQL = @RebuildIndexesSQL +
CASE
 WHEN [Fragmentation%] > 30
   THEN CHAR(10) + 'ALTER INDEX ' + QUOTENAME(IndexName) + ' ON '
      + QUOTENAME(SchemaName) + '.'
      + QUOTENAME(TableName) + ' REBUILD;'
 WHEN [Fragmentation%] > 10
    THEN CHAR(10) + 'ALTER INDEX ' + QUOTENAME(IndexName) + ' ON '
    + QUOTENAME(SchemaName) + '.'
    + QUOTENAME(TableName) + ' REORGANIZE;'
END
FROM #FragmentedIndexes
WHERE [Fragmentation%] > 10
DECLARE @StartOffset INT
DECLARE @Length INT
SET @StartOffset = 0
SET @Length = 4000
WHILE (@StartOffset < LEN(@RebuildIndexesSQL))
BEGIN
 PRINT SUBSTRING(@RebuildIndexesSQL, @StartOffset, @Length)
 SET @StartOffset = @StartOffset + @Length
END
PRINT SUBSTRING(@RebuildIndexesSQL, @StartOffset, @Length)
EXECUTE sp_executesql @RebuildIndexesSQL
DROP TABLE #FragmentedIndexes

另请注意,此脚本可能会运行一段时间并阻止对表的访问。除非您拥有企业版,否则SQL可以在重建索引时锁定表。这将使用索引阻止对该表的所有查询,直到索引碎片整理完成。因此,不建议仅在维护窗口期间的运行时间内运行索引重建。如果您正在运行企业版,则可以使用ONLINE = ON选项对联机索引进行碎片整理。这将占用更多空间,但在碎片整理操作期间不会阻止/锁定表。

如果您需要更多信息,请大声说。

更新:

如果在较小的数据库上运行此查询,则可以在调用sys.dm_db_index_physical_stats时使用“DETAILED”参数。这可能是对索引的更详细的检查。评论中的讨论还将指出,在更大的表上,可能值得进行SAMPLED扫描,因为这将有助于减少进行索引扫描所需的时间。

答案 1 :(得分:3)

如果您考虑避免创建任何临时表并解析字符串以创建SQL字符串列表。这是实现它的有效方法:

USE databasename

GO

DECLARE @Queryresult NVARCHAR(4000)

SET @Queryresult=''

SELECT

  @Queryresult=@Queryresult + 'ALTER INDEX ' + QUOTENAME(i.name) + ' ON '
  + QUOTENAME('dbo') + '.'
  + QUOTENAME(OBJECT_NAME(i.OBJECT_ID)) + ' REBUILD;'

 FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'SAMPLED') ss

  INNER JOIN sys.indexes i ON i.OBJECT_ID = ss.OBJECT_ID AND i.index_id = ss.index_id

  INNER JOIN sys.objects o ON ss.object_id = o.object_id

WHERE ss.avg_fragmentation_in_percent > 50

AND ss.record_count > 0 

AND o.is_ms_shipped = 0 --Excludes any objects created as a part of SQL Server installation

AND ss.index_id > 0     --Excludes heap indexes

EXEC sp_executesql @Queryresult

答案 2 :(得分:1)

是的,你可以。

您可以使用此查询获取碎片索引:

SELECT OBJECT_NAME(i.OBJECT_ID) AS TableName,
i.name AS IndexName,
indexstats.avg_fragmentation_in_percent
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'DETAILED') indexstats
INNER JOIN sys.indexes i ON i.OBJECT_ID = indexstats.OBJECT_ID
AND i.index_id = indexstats.index_id
WHERE indexstats.avg_fragmentation_in_percent > 20

并根据结果只需构建一个命令来重新创建它们

我会将所有内容都包装在存储过程中并从SQL Server作业中调用它

仅供参考,50%是一个非常大的碎片。我会少用。

答案 3 :(得分:1)

您可以使用sys.dm_db_index_physical_stats获取有关索引碎片的信息(请参阅avg_fragmentation_in_percent列)。然后,只要达到阈值,就可以使用rebuild子句执行alter index。