我希望有人可以帮我这个脚本。 我想创建一个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:如果您有任何其他建议或在脚本中发现错误 - 为了改进代码,请告诉我。
答案 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
...