SQL脚本永远执行 - 可能循环?

时间:2015-08-20 07:48:11

标签: sql sql-server tsql

我希望有人可以帮我这个脚本。 我想创建一个SQL脚本,使用“online ON”选项重建所有非聚集索引,以防止在重建索引时锁定表,也不使用游标(这会减慢速度)。出于这个原因,我使用了While循环(至少是大多数SQL开发人员建议的),所以我编写了一个SQL脚本。 我注意到,SQL脚本永远执行。在一台试验机上,我让它运行了12分钟,之后我中止了操作...

请问一些SQL专家请告诉我是什么导致瓶颈? 有没有更好的方法来做到这一点,或者可能“微调”当前的脚本?

旧代码示例:

USE MASTER
GO
DECLARE @DbName AS VARCHAR(50),
    @DBIndexName AS VARCHAR(250), 
    @DBTableName AS VARCHAR(100), 
    @AlterCommand AS VARCHAR(500),
    @SwitchDB AS NVARCHAR(50)

SELECT name INTO #DatabaseList FROM master..sysdatabases
WHILE EXISTS (SELECT * FROM #DatabaseList)
BEGIN
    SELECT TOP 1 @DbName = name FROM #DatabaseList
    ORDER BY name ASC

    SET @SwitchDB = 'USE '+@DbName
    EXEC(@SwitchDB)

    -- pronaći sve index-e koji imaju fragmentaciju veću od 10%, te staviti u tablicu
    SELECT object_name(dt.object_id) Tablename,si.name
    IndexName,dt.avg_fragmentation_in_percent AS
    ExternalFragmentation,dt.avg_page_space_used_in_percent AS
    InternalFragmentation
    INTO #FragmIndex
    FROM
    (
        SELECT object_id,index_id,avg_fragmentation_in_percent,avg_page_space_used_in_percent
        FROM sys.dm_db_index_physical_stats (db_id(@DbName),null,null,null,'DETAILED')  
        WHERE index_id <> 0
    ) AS dt INNER JOIN sys.indexes si ON si.object_id=dt.object_id
    AND si.index_id=dt.index_id AND dt.avg_fragmentation_in_percent>10
    AND dt.avg_page_space_used_in_percent<75 and si.type_desc = 'NONCLUSTERED' ORDER BY avg_fragmentation_in_percent DESC 

    -- Napraviti petlju koja će izvršiti rebuild svih indexa
    SET @SwitchDB = 'USE master'
    EXEC (@SwitchDB)

    WHILE EXISTS (SELECT * FROM #FragmIndex)
    BEGIN
        SELECT TOP 1 @DBIndexName = IndexName, @DBTableName = Tablename FROM #FragmIndex
        ORDER BY IndexName ASC

        -- rebuild index command setiranje na "ONLINE ON" kako bi se izbjeglo "zaključavanje tablice".
        SET @AlterCommand ='ALTER INDEX '+@DBIndexName+' ON '+ @DbName+'.dbo.'+@DbTableName +'REBUILD WITH (FILLFACTOR=80,STATISTICS_NORECOMPUTE = ON,ONLINE=ON)'
        EXEC(@AlterCommand)

        DELETE #FragmIndex 
        WHERE IndexName = @DBIndexName
    END

    DELETE #DatabaseList 
    WHERE name = @DbName
END
DROP TABLE #DatabaseList
GO

非常感谢,最诚挚的问候。

只是添加新的“更正代码” - 对于所有希望使用它的人,修改它或其他......:)

USE MASTER
GO
DECLARE @DbName AS VARCHAR(50),
    @DBIndexName AS VARCHAR(250), 
    @DBTableName AS VARCHAR(100), 
    @AlterCommand AS VARCHAR(500),
    @SwitchDB AS NVARCHAR(50),
    @numEntries AS BIGINT,
    @numIndexEntries AS BIGINT

SELECT name INTO #DatabaseList FROM master..sysdatabases
SET @numEntries = (SELECT COUNT(*) FROM #DatabaseList)

WHILE @numEntries > 0 --EXISTS (SELECT * FROM #DatabaseList)
BEGIN
    SELECT TOP 1 @DbName = name FROM #DatabaseList
    ORDER BY name ASC

    SET @SwitchDB = 'USE '+@DbName
    EXEC(@SwitchDB)

    -- pronaći sve index-e koji imaju fragmentaciju veću od 10%, te staviti u tablicu
    SELECT object_name(dt.object_id) Tablename,si.name
    IndexName,dt.avg_fragmentation_in_percent AS
    ExternalFragmentation,dt.avg_page_space_used_in_percent AS
    InternalFragmentation
    INTO #FragmIndex
    FROM
    (
        SELECT object_id,index_id,avg_fragmentation_in_percent,avg_page_space_used_in_percent
        FROM sys.dm_db_index_physical_stats (db_id(@DbName),null,null,null,'DETAILED')  
        WHERE index_id <> 0
    ) AS dt INNER JOIN sys.indexes si ON si.object_id=dt.object_id
    AND si.index_id=dt.index_id AND dt.avg_fragmentation_in_percent>10
    AND dt.avg_page_space_used_in_percent<75 and si.type_desc = 'NONCLUSTERED' ORDER BY avg_fragmentation_in_percent DESC 

    -- Napraviti petlju koja će izvršiti rebuild svih indexa
    SET @SwitchDB = 'USE master'
    EXEC (@SwitchDB)

    SET @numIndexEntries = (SELECT COUNT(*) FROM #FragmIndex)
    WHILE @numIndexEntries > 0 --EXISTS (SELECT * FROM #FragmIndex)
    BEGIN
        SELECT TOP 1 @DBIndexName = IndexName, @DBTableName = Tablename FROM #FragmIndex
        ORDER BY IndexName ASC

        -- rebuild index command setiranje na "ONLINE ON" kako bi se izbjeglo "zaključavanje tablice".
        SET @AlterCommand ='ALTER INDEX '+@DBIndexName+' ON '+ @DbName+'.dbo.'+@DbTableName +'REBUILD WITH (FILLFACTOR=80,STATISTICS_NORECOMPUTE = ON,ONLINE=ON)'
        EXEC(@AlterCommand)

        DELETE #FragmIndex WHERE IndexName = @DBIndexName
        SET @numIndexEntries = (SELECT COUNT(*) FROM #FragmIndex)
    END

    DELETE #DatabaseList WHERE name = @DbName
    SET @numEntries = (SELECT COUNT(*) FROM #DatabaseList)
END
DROP TABLE #DatabaseList
GO

P.S:如果您有任何其他建议或在脚本中发现错误 - 为了改进代码,请告诉我。

1 个答案:

答案 0 :(得分:1)

两个可能的问题可能会导致这种影响。

首先,DELETE语句可能不会删除任何内容。您可以通过PRINT #DatabaseList语句后DELETE中的条目数轻松检查此内容。

其次,EXISTS (SELECT * FROM #DatabaseList)只能被评估一次,而不是每次循环迭代。

我将其更改如下:

...

DECLARE @numEntries BIGINT
SET @numEntries = (SELECT COUNT(*) FROM #DatabaseList)

WHILE @numEntries > 0
BEGIN
    ...

    DELETE #DatabaseList WHERE name = @DbName
    SET @numEntries = (SELECT COUNT(*) FROM #DatabaseList)
END

...