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 公衆號,將有更多精彩內容分享: