嵌套游标的动态重建索引

时间:2017-10-08 21:22:07

标签: sql sql-server tsql indexing cursors

我正在尝试创建一个存储过程来重建所有数据库的所有索引,碎片大于> 30%

DECLARE @SQL nvarchar(max)
DECLARE @dbname VARCHAR(50)
DECLARE @dbid VARCHAR(50)
DECLARE @SQL2 nvarchar(max)

--Cursor for database names
DECLARE db_cursor CURSOR READ_ONLY FOR  
SELECT name, database_id    FROM sys.databases
WHERE name NOT IN ('master','model','msdb','tempdb')

--opening the cursor and go to first row
OPEN db_cursor   
FETCH NEXT FROM db_cursor INTO @dbname, @dbid 

WHILE @@FETCH_STATUS = 0   
    BEGIN   
    -- Dynamic query to fill @MyIndexFragmented with data
    SET @SQL= 'USE ' + @dbname  + CHAR(13) + 
     '
     DECLARE @IndexName varchar(150) 
     DECLARE @TableName varchar(150)
     DECLARE @Filas INTEGER
     --Declare Table Variable
     DECLARE @MyIndexFragmented TABLE  
     (Databasename varchar(50),  
     TableName varchar(200),  
     IndexName varchar(200), 
     Avg_Fragmentation Decimal, 
     Page_Count INT ) 

     INSERT INTO @MyIndexFragmented
       SELECT DB_NAME(database_id) AS DatabaseName, 
        OBJECT_NAME(ips.object_id) AS TableName, 
        i.name AS IndexName,
        avg_fragmentation_in_percent, page_count
            FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, ''LIMITED'') ips
            INNER JOIN sys.indexes i 
                ON i.object_id = ips.object_id 
                AND i.index_id = ips.index_id
            INNER JOIN sys.partitions p 
                ON p.object_id = i.object_id 
                AND p.index_id = i.index_id
            WHERE avg_fragmentation_in_percent >= 30 
            AND ips.index_id > 0 
            AND page_count > 1000
            ORDER BY avg_fragmentation_in_percent DESC
            set @filas= (select count (*) from @MyIndexFragmented)
            IF @filas>0 
            BEGIN 
            -- nested cursor
                DECLARE index_cursor CURSOR FOR  
                SELECT IndexName, TableName FROM @MyIndexFragmented 

                OPEN index_cursor   
                -- Nos vamos a la primera fila
                FETCH NEXT FROM index_cursor INTO  @Indexname, @TableName
                WHILE @@FETCH_STATUS = 0   
                BEGIN  
                    ALTER INDEX  @IndexName  ON  @TableName  REBUILD
                    --Next Row
                    FETCH NEXT FROM index_cursor INTO  @Indexname, @TableName
                END  
                 CLOSE index_cursor
                 DEALLOCATE index_cursor
            END -- FOR FILAS >0'

      EXECUTE SP_EXECUTESQL @SQL

       FETCH NEXT FROM db_cursor INTO @dbname, @dbid
    end
CLOSE db_cursor   
DEALLOCATE db_cursor

我收到了这个错误:

  

'@IndexName'附近的语法不正确。

所以我找不到动态制作的方法:

ALTER INDEX @IndexName ON @TableName REBUILD

有什么建议吗?

全部谢谢

3 个答案:

答案 0 :(得分:1)

这是我的最终代码,正在运作。如果有人试图重建/重新组织所有数据库上的所有碎片索引(> 5< 30 - 重新组织) (> 30重建) 这是代码

{{1}}

答案 1 :(得分:0)

如果碎片超过30%,这个动态代码将帮助您重新构建索引,此代码可能会帮助您(尝试避免将光标应用于表上的锁定)

DECLARE @command nvarchar(max),@Satetement nvarchar(max)

IF OBJECT_ID('tempdb..##TableList') IS NOT NULL 
DROP TABLE ##TableList  
CREATE TABLE ##TableList 
(
ID INT IDENTITY
,DbName varchar(100)
,TableList nvarchar(100)
,IndexName nvarchar(500)
,SchemaName nvarchar(500)
)
SET @Satetement ='
 INSERT INTO ##TableList(DbName,TableList,IndexName,SchemaName)  
  SELECT 
      DB_NAME(DB_ID()),
      dbtables.[name],
      dbindexes.[name],
      dbschemas.[name]   
 FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL, NULL, NULL) AS indexstats 
 INNER JOIN sys.tables dbtables on dbtables.[object_id] = indexstats.[object_id] 
 INNER JOIN sys.schemas dbschemas on dbtables.[schema_id] = dbschemas.[schema_id] 
 INNER JOIN sys.indexes AS dbindexes ON dbindexes.[object_id] = indexstats.[object_id] 
 AND indexstats.index_id = dbindexes.index_id WHERE indexstats.database_id = DB_ID() 
 AND indexstats.avg_fragmentation_in_percent >=30 
 AND indexstats.index_id > 0 
 AND page_count > 1000
 '
 SELECT @command = 'IF ''?'' IN (''SqlClass'',''AdventureWorks2012'') BEGIN USE ? EXEC ('''+ @Satetement+''') END ' +CHAR(13)+CHAR(10) 

 PRINT @command

EXEC sp_MSforeachdb @command

SET @command=''

DECLARE @fillfactor INT =85
        ,@MinID INT
        ,@MaxID INT
        ,@Table VARCHAR(100)
        ,@Database VARCHAR(100)
        ,@GetIndex VARCHAR(500)
        ,@GetSchema VARCHAR(500)
        ,@SQl nvarchar(max)

SELECT @MinID=MIN(Id) ,@MaxID=MAX(Id) from ##TableList  where IndexName IS NOT NULL

WHILE (@MinID<=@MaxID)
Begin
SELECT DISTINCT @Table=TableList,
                @Database=DbName,
                @GetIndex=IndexName,
                @GetSchema=SchemaName

        FROM ##TableList where id=@MinID AND IndexName IS NOT NULL

  SET @command = ' ALTER INDEX '+@GetIndex+' ON [' + @Database+']'+'.['+@GetSchema+ ']'+'.['+@Table +']'+ ' REBUILD WITH (FILLFACTOR = ' + CONVERT(VARCHAR(3),@fillfactor) + ')' 

  EXEC ( @command)
  PRINT @GetIndex +' index was Rebuild'
  --PRINT (@command) 

  SET @MinID=@MinID+1
END

执行上述脚本后,检查索引的更改填充因子值运行以下查询

SELECT 

      DB_NAME() AS Database_Name
    , sc.name AS Schema_Name
    , o.name AS Table_Name
    , o.type_desc
    , i.name AS Index_Name
    , i.type_desc AS Index_Type
    , i.fill_factor
FROM sys.indexes i
INNER JOIN sys.objects o ON i.object_id = o.object_id
INNER JOIN sys.schemas sc ON o.schema_id = sc.schema_id
WHERE i.name IS NOT NULL
AND o.type = 'U'
AND i.fill_factor not in (0, 100)
ORDER BY i.fill_factor DESC, o.name

答案 2 :(得分:0)

非常感谢您的早期回复。 Sreenu131,你的建议对我来说非常好。

我只是改变了一行,以便整理所有数据库(系统数据库除外)

SELECT @command = 'IF ''?'' NOT IN (''msdb'', ''master'',''model'',''tempdb'' ) BEGIN USE ? EXEC ('''+ @Satetement+''') END ' +CHAR(13)+CHAR(10) 

非常感谢!!!