SQL Server 索引管理——索引備份

SQL Server 索引管理——索引備份

作爲一個DBA,做任何操作(尤其是刪除、更新等)之前,首先要想到事情是,操作如何還原,對於索引的操作也是如此。前文分別敘述了無用索引刪除腳本的生成、以及禁用腳本的生成。對於禁用的索引,只需要將禁用腳本中的disable改爲REBUILD即可重建恢復。而禁用腳本,在最終確認無用時,還是要刪除掉的,所以對於這類要刪除的索引,在刪除之前,我們還是要備份其創建腳本的。下面我直接給出生成創建索引腳本的兩個過程:

/*
	獲取索引的鍵列、包含列、分區列信息
*/
CREATE PROCEDURE dba_getIndexColomnStr
	@objectId INT
	,@indexId INT
	,@isPartion BIT		--0表示索引未分區,1表示索引分區
	,@keyColumnStr VARCHAR(100) OUTPUT
	,@includeColumnStr VARCHAR(100) OUTPUT
	,@partitonColumnName VARCHAR(50) OUTPUT
AS
	
BEGIN
SET NOCOUNT ON;
SELECT c.[object_id],index_id,name column_name,key_ordinal,partition_ordinal,is_descending_key,is_included_column
INTO #temp
FROM sys.index_columns ic
LEFT JOIN sys.columns c ON ic.[object_id]=c.[object_id] AND ic.column_id=c.column_id
WHERE ic.[object_id]=@objectId AND index_id=@indexId;

 SELECT @keyColumnStr=STUFF((
                     SELECT column_name+CASE WHEN is_descending_key=0 THEN ' ASC ' ELSE ' DESC' END +N','
                     FROM #temp
                     WHERE is_included_column=0 AND key_ordinal>0
                     ORDER BY key_ordinal
                     FOR XML PATH(''),TYPE).value('.','varchar(max)'),1,0,'');
SELECT @includeColumnStr=STUFF((
                     SELECT column_name + N','
                     FROM #temp
                     WHERE is_included_column=1
                     ORDER BY key_ordinal
                     FOR XML PATH(''),TYPE).value('.','varchar(max)'),1,0,'');
SELECT @keyColumnStr=LEFT(@keyColumnStr,LEN(@keyColumnStr)-1),@includeColumnStr=LEFT(@includeColumnStr,LEN(@includeColumnStr)-1);
	IF @isPartion=1 
		SELECT 
			@partitonColumnName=column_name
		FROM #temp
		WHERE partition_ordinal>0;
	ELSE
		SET @partitonColumnName='';

	TRUNCATE TABLE #temp;
	DROP TABLE #temp;

END;
GO

此過程傳入對象編號和索引編號,返回索引鍵列、包含列字符串,如果索引有分區,還會返回分區列。下面是生成索引創建腳本的過程:

/*
	生成索引的創建腳本
	測試
	exec dba_generateCreateCodeOfDisabledIndexes 0
*/
ALTER PROCEDURE dba_generateCreateCodeOfDisabledIndexes
	@isDisableIndex BIT=1	--0生成數據庫下所有表的所有索引的創建腳本,1生成數據庫中禁用索引的創建腳本,默認值爲1
AS
BEGIN
DECLARE @createIndexCode VARCHAR(MAX)
	,@keyColumnStr VARCHAR(100)		--索引鍵列組(包括升降序)
	,@includeColumnStr VARCHAR(100)	--索引包含列組
	,@objectId INT
	,@indexId INT
	,@indexName VARCHAR(100)
	,@indexTypeDesc VARCHAR(50)	--索引類型
	,@isUnique VARCHAR(10)		--是否唯一索引
	,@ignoreDupKey BIT	--是否忽略重複鍵
	,@isPadded BIT	--是否使用空格對齊
	,@isPrimaryKey BIT	--是否爲主鍵
	,@allowRowLock BIT	--是否允許行鎖
	,@allowPageLock BIT	--是否允許頁鎖
	,@filterDefinition VARCHAR(50)	--過濾定義
	,@fillFactor INT	--填充因子
	,@partitionSchemesName VARCHAR(50)	--分區方案名
	,@partitionColumnName VARCHAR(50)	--分區列
	,@indexOptions VARCHAR(200)			--索引配置選項
	,@fileGroupName VARCHAR(50)			--索引所在的文件組
	,@schemaName VARCHAR(50)
	,@tableName VARCHAR(50)
	,@indexType INT
	,@compressionDelay INT
	,@sql VARCHAR(MAX);

--索引壓縮、索引分區、內存表索引,聚集索引、非聚集索引、唯一索引,主鍵索引

--無分區的索引創建腳本生成
DECLARE cur CURSOR FOR(
SELECT t.[object_id],ix.index_id,ix.TYPE indexType,QUOTENAME(SCHEMA_NAME(t.SCHEMA_ID),'[') [schema_name], QUOTENAME(t.name,'['), QUOTENAME(ix.name,'['),
	 CASE WHEN ix.is_unique = 1 THEN 'UNIQUE ' ELSE '' END is_unique
	 ,ix.is_primary_key
	 ,ix.type_desc,
	 CASE WHEN ix.is_padded=1 THEN 'PAD_INDEX = ON, ' ELSE 'PAD_INDEX = OFF, ' END
	 + CASE WHEN ix.ALLOW_PAGE_LOCKS=1 THEN 'ALLOW_PAGE_LOCKS = ON, ' ELSE 'ALLOW_PAGE_LOCKS = OFF, ' END
	 + CASE WHEN ix.ALLOW_ROW_LOCKS=1 THEN  'ALLOW_ROW_LOCKS = ON, ' ELSE 'ALLOW_ROW_LOCKS = OFF, ' END
	 + CASE WHEN INDEXPROPERTY(t.OBJECT_ID, ix.name, 'IsStatistics') = 1 THEN 'STATISTICS_NORECOMPUTE = ON, ' ELSE 'STATISTICS_NORECOMPUTE = OFF, ' END
	 + CASE WHEN ix.IGNORE_DUP_KEY=1 THEN 'IGNORE_DUP_KEY = ON, ' ELSE 'IGNORE_DUP_KEY = OFF' END
	 +CASE p.data_compression_desc WHEN 'page' THEN ', DATA_COMPRESSION = PAGE' 
			WHEN 'row' THEN ', DATA_COMPRESSION = ROW'
			ELSE '' END
	 + CASE WHEN is_primary_key=1 THEN '' ELSE ', SORT_IN_TEMPDB = OFF' END	--創建主鍵索引不能加此參數
	 +CASE WHEN ix.fill_factor>0 THEN ', FILLFACTOR =' + CAST(ix.fill_factor AS VARCHAR(6)) ELSE '' END AS IndexOptions
	  ,QUOTENAME(FILEGROUP_NAME(ix.data_space_id),'[') FileGroupName--,p.data_compression_desc
 FROM sys.tables t 
 INNER JOIN sys.indexes ix ON t.OBJECT_ID=ix.OBJECT_ID 
 INNER JOIN sys.partitions p  ON p.[object_id]=ix.[object_id] AND p.index_id=ix.index_id
 WHERE ix.data_space_id>0	--=0是內存表的索引
	AND ix.data_space_id<65536	-->65536 爲分區索引
	AND ix.TYPE IN(1,2,5,6)	--1聚集索引,2非聚集索引,5聚集列索引,6非聚集列索引
	AND (CASE WHEN @isDisableIndex=1 THEN ix.is_disabled ELSE 1 END)=1
 --order by schema_name(t.schema_id), t.name, ix.name
 );
 OPEN cur;
 FETCH NEXT FROM cur INTO @objectId,@indexId,@indexType,@schemaName,@tableName,@indexName,@isUnique,@isPrimaryKey,@indexTypeDesc,@indexOptions,@fileGroupName;
 DECLARE @desc VARCHAR(50);
 SET @desc='沒有不分區的'+(CASE WHEN @isDisableIndex=1 THEN '禁用' ELSE ''  END) +'索引';
  IF @@fetch_status < 0
    BEGIN
		CLOSE cur;
        DEALLOCATE cur;
        RAISERROR(15472,-1,-1,@desc); -- 沒有該類型的索引.
        --return (0)
		GOTO  partitionIndex;
    END;

 WHILE @@FETCH_STATUS=0
 BEGIN
	EXECUTE dba_getIndexColomnStr @objectId,@indexId,0,@keyColumnStr OUTPUT,@includeColumnStr OUTPUT,@partitionColumnName OUTPUT;
	IF @indexType IN(1,2)	--行索引
	BEGIN
		IF @isPrimaryKey=1	--主鍵索引
			SET @sql='ALTER TABLE ' +@schemaName+'.'+@tableName +' ADD CONSTRAINT '+@indexName+' PRIMARY KEY '+@indexTypeDesc
				+'('+@keyColumnStr+') '
				+'WITH('+@indexOptions+')'
				+' ON ' +@fileGroupName;
		ELSE	--非主鍵索引
			SET @sql='CREATE '+@isUnique +' '+ @indexTypeDesc +' INDEX '+@indexName+' ON '+@schemaName+'.'+@tableName
					+' (' +@keyColumnStr+') '
					+CASE WHEN @includeColumnStr IS NULL THEN ' ' ELSE ' INCLUDE( '+@includeColumnStr+') ' END
					+'WITH('+@indexOptions+') '
					+'ON ' +@fileGroupName;
	END;
	ELSE IF @indexType IN(5,6)	--列索引
	BEGIN
		SET @sql='CREATE '+@indexTypeDesc+' INDEX '+@indexName+ ' ON ' +@schemaName+'.'+@tableName 
			+'('+@includeColumnStr+') '
			+' ON ' +@fileGroupName;
	END;
	PRINT @sql;
	FETCH NEXT FROM cur INTO @objectId,@indexId,@indexType,@schemaName,@tableName,@indexName,@isUnique,@isPrimaryKey,@indexTypeDesc,@indexOptions,@fileGroupName;
 END;
 CLOSE cur;
 DEALLOCATE cur;

 --分區索引
 partitionIndex:;
DECLARE cur1 CURSOR FOR(
SELECT t.[object_id],ix.index_id,ix.TYPE indexType,QUOTENAME(SCHEMA_NAME(t.SCHEMA_ID),'[') [schemaName]
	, QUOTENAME(t.name,'[') tableName, QUOTENAME(ix.name,'[') indexName
	, CASE WHEN ix.is_unique = 1 THEN 'UNIQUE ' ELSE '' END is_unique
	 ,ix.is_primary_key
	 ,ix.type_desc,
	 CASE WHEN ix.is_padded=1 THEN 'PAD_INDEX = ON, ' ELSE 'PAD_INDEX = OFF, ' END
	 + CASE WHEN ix.ALLOW_PAGE_LOCKS=1 THEN 'ALLOW_PAGE_LOCKS = ON, ' ELSE 'ALLOW_PAGE_LOCKS = OFF, ' END
	 + CASE WHEN ix.ALLOW_ROW_LOCKS=1 THEN  'ALLOW_ROW_LOCKS = ON, ' ELSE 'ALLOW_ROW_LOCKS = OFF, ' END
	 + CASE WHEN INDEXPROPERTY(t.OBJECT_ID, ix.name, 'IsStatistics') = 1 THEN 'STATISTICS_NORECOMPUTE = ON, ' ELSE 'STATISTICS_NORECOMPUTE = OFF, ' END
	 + CASE WHEN ix.IGNORE_DUP_KEY=1 THEN 'IGNORE_DUP_KEY = ON, ' ELSE 'IGNORE_DUP_KEY = OFF ' END
	 + CASE WHEN is_primary_key=1 THEN '' ELSE ', SORT_IN_TEMPDB = OFF' END	--創建主鍵索引不能加此參數
	 +CASE WHEN ix.fill_factor>0 THEN ', FILLFACTOR =' + CAST(ix.fill_factor AS VARCHAR(6)) ELSE '' END AS IndexOptions
	  ,ps.name partitonSchemesName--,fg.groupname FileGroupName
	  ,ix.compression_delay
 FROM sys.tables t 
 INNER JOIN sys.indexes ix ON t.OBJECT_ID=ix.OBJECT_ID
 INNER JOIN sys.partition_schemes ps ON ps.data_space_id=ix.data_space_id
 WHERE ix.TYPE IN(1,2,5,6)	--1聚集索引,2非聚集索引
	AND ix.data_space_id>65536	--分區索引
	AND (CASE WHEN @isDisableIndex=1 THEN ix.is_disabled ELSE 1 END)=1
);
OPEN cur1;
FETCH NEXT FROM cur1 INTO @objectId,@indexId,@indexType,@schemaName,@tableName,@indexName,@isUnique,@isPrimaryKey
	,@indexTypeDesc,@indexOptions,@partitionSchemesName,@compressionDelay;
SET @desc='不存在分區的'+(CASE WHEN @isDisableIndex=1 THEN '禁用' ELSE '' END) +'索引'; 
  IF @@fetch_status < 0
    BEGIN
		CLOSE cur1;
        DEALLOCATE cur1;
        RAISERROR(15472,-1,-1,@desc); -- 沒有該類型的索引.
        --return (0)
		GOTO memoryOptimizedTableIndex;
    END;

	WHILE @@FETCH_STATUS=0
 BEGIN
	EXECUTE dba_getIndexColomnStr @objectId,@indexId,1,@keyColumnStr OUTPUT,@includeColumnStr OUTPUT,@partitionColumnName OUTPUT;
	IF @indexType IN(1,2)
	BEGIN
		IF @isPrimaryKey=1	--主鍵索引
			SET @sql='ALTER TABLE ' +@schemaName+'.'+@tableName +' ADD CONSTRAINT '+@indexName+' PRIMARY KEY '+@indexTypeDesc
				+'('+@keyColumnStr+') '
				+'WITH('+@indexOptions+')'
				+' ON ' +@fileGroupName;
		ELSE	--非主鍵索引
			SET @sql='CREATE '+@isUnique +' '+ @indexTypeDesc +' INDEX '+@indexName+' ON '+@schemaName+'.'+@tableName
					+' (' +@keyColumnStr+') '
					+CASE WHEN @includeColumnStr IS NULL THEN ' ' ELSE ' INCLUDE( '+@includeColumnStr+') ' END
					+'WITH('+@indexOptions+') '
					+'ON ' +@partitionSchemesName+'('+@partitionColumnName+')';
		PRINT @sql;
		--生成分區的壓縮腳本
		SELECT @sql=STUFF((
						SELECT 
							'ALTER INDEX '+@indexName+' ON '+@schemaName+'.'+@tableName+' REBUILD PARTITION = '+CONVERT(VARCHAR(5),partition_number)+
								' WITH (SORT_IN_TEMPDB = OFF, ONLINE = OFF, DATA_COMPRESSION = '+data_compression_desc+ ' ) '+CHAR(10)
						FROM sys.partitions
						WHERE [object_id]=@objectId AND index_id=@indexId AND data_compression>0
						FOR XML PATH(''),TYPE).value('.','varchar(max)'),1,0,'');
		PRINT @sql;
	END;
	ELSE IF @indexType IN(5,6)	--列索引
	BEGIN
		SET @sql='CREATE '+@indexTypeDesc+' INDEX '+@indexName+ ' ON ' +@schemaName+'.'+@tableName 
			+'('+@includeColumnStr+') '
			+' ON ' +@partitionSchemesName+'('+@partitionColumnName+')';
		PRINT @sql;
		--列索引被禁用後,其在sys.partitions 中的信息消失
		--生成列索引的壓縮腳本
		--SELECT @sql=STUFF((
		--				SELECT 
		--					'ALTER INDEX '+@indexName+' ON '+@schemaName+'.'+@tableName+' REBUILD PARTITION = '+CONVERT(VARCHAR(5),partition_number)+
		--						' WITH (DATA_COMPRESSION = '+data_compression_desc+ ' ) '+CHAR(10)
		--				FROM sys.partitions
		--				WHERE [object_id]=@objectId AND index_id=@indexId AND data_compression>0
		--				FOR XML PATH(''),TYPE).value('.','varchar(max)'),1,0,'');
		--PRINT @sql;
	END;
	FETCH NEXT FROM cur1 INTO @objectId,@indexId,@indexType,@schemaName,@tableName,@indexName,@isUnique,@isPrimaryKey
	,@indexTypeDesc,@indexOptions,@partitionSchemesName,@compressionDelay;
 END;
 CLOSE cur1;
 DEALLOCATE cur1;

 columnIndex:;


 memoryOptimizedTableIndex:;

  --內存表索引創建腳本生成
  --內存表索引不能禁用,這裏給出的是備份索引的腳本
  DECLARE cur2 CURSOR FOR(
SELECT t.[object_id],ix.index_id
	,QUOTENAME(SCHEMA_NAME(t.SCHEMA_ID),'[') [schemaName]
	, QUOTENAME(t.name,'[') tableName
	, QUOTENAME(ix.name,'[') indexName
	,ix.type_desc
 FROM sys.tables t 
 INNER JOIN sys.indexes ix ON t.OBJECT_ID=ix.OBJECT_ID 
 INNER JOIN sys.partitions p  ON p.[object_id]=ix.[object_id] AND p.index_id=ix.index_id
 WHERE ix.data_space_id=0	--=0是內存表的索引
	AND ix.is_primary_key=0
	AND (CASE WHEN @isDisableIndex=1 THEN ix.is_disabled ELSE 1 END)=1
 --order by schema_name(t.schema_id), t.name, ix.name
 );
 OPEN cur2;
 FETCH NEXT FROM cur2 INTO @objectId,@indexId,@schemaName,@tableName,@indexName,@indexTypeDesc;
 --DECLARE @desc VARCHAR(50);
 SET @desc='沒有不分區的'+(CASE WHEN @isDisableIndex=1 THEN '禁用' ELSE ''  END) +'索引';
  IF @@fetch_status < 0
    BEGIN
		CLOSE cur2;
        DEALLOCATE cur2;
        RAISERROR(15472,-1,-1,@desc); -- 沒有該類型的索引.
        --return (0)
		GOTO  pEnd;
    END;

	 WHILE @@FETCH_STATUS=0
	 BEGIN
		EXECUTE dba_getIndexColomnStr @objectId,@indexId,0,@keyColumnStr OUTPUT,@includeColumnStr OUTPUT,@partitionColumnName OUTPUT;
	
				SET @sql='ALTER TABLE ' +@schemaName+'.'+@tableName +' ADD INDEX '+@indexName+' '+@indexTypeDesc
					+'('+@keyColumnStr+') ';	

		PRINT @sql;
		 FETCH NEXT FROM cur2 INTO @objectId,@indexId,@schemaName,@tableName,@indexName,@indexTypeDesc;
	 END;
	 CLOSE cur2;
	 DEALLOCATE cur2;
	 pEnd:;
 END;

注意該過程可以生成行索引、列索引以及內存優化表索引創建腳本的生成,對於xml索引、空間索引、全文索引暫未包括,請注意使用。

如果喜歡,可以搜索關注 MSSQLServer 公衆號,將有更多精彩內容分享:

                                                                 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章