SQL Server 索引重新生成 索引重建 定期维护索引

SQL Server 索引重新生成 索引重建 定期维护索引
最新回答
浅墨时光

2020-06-25 06:09:49

玩数据的都知道索引的重要性。索引对数据库引擎快速返回查询结果有巨大作用,是数据库优化的关键手段。然而,当对索引所在的基础数据表进行插入、删除和更新等操作时,索引的逻辑排序和基础表的物理排序可能不匹配,从而产生索引碎片。索引碎片增多会导致查询响应时间变长和查询性能下降。SQL Server 提供了两种修复索引碎片的方法:重新组织索引和重新生成索引。

重新组织索引是对最外层数据页里的数据进行重新排序,并压缩索引页,不会添加额外数据。该过程不会占用很多系统资源,在运行过程中外部进程仍能访问数据表,因此称为联机执行。执行 SQL 语句为:ALTER INDEX 索引名 on 表名 REBUILD。

重新生成索引是删除目标索引并创建一个新索引。旧索引中的碎片将随之删除,新索引的逻辑排序将与对象的物理排序相匹配。整个过程需要删除索引并重新创建,外部进程无法访问数据表,这是一大障碍。执行 SQL 语句为:ALTER INDEX 索引名 on 表名 REBUILD。

何时进行索引的重新组织或重新生成?根据 MSN 网站的推荐,当索引碎片比例在 5% 以下时,不需要考虑进行整理。当碎片比例在 5% 至 30% 之间时,建议进行索引重新组织。超过 30% 时,需要进行索引重新生成。

MSDN 网站提供了一个存储过程,用于索引碎片整理。可以在程序或数据库维护计划中定期执行,以优化数据库性能。建议在数据库里创建定期任务自动整理索引,或者通过 SSIS 作业定期维护索引。

执行存储过程的 SQL 代码如下:

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE proc [dbo].[reBuildIndex]

as

DECLARE @objectid int;

DECLARE @indexid int;

DECLARE @partitioncount bigint;

DECLARE @schemaname sysname;

DECLARE @objectname sysname;

DECLARE @indexname sysname;

DECLARE @partitionnum bigint;

DECLARE @partitions bigint;

DECLARE @frag float;

DECLARE @command varchar(8000);

IF EXISTS (SELECT name FROM sys.objects WHERE name = 'work_to_do')

DROP TABLE work_to_do;

SELECT

object_id AS objectid,

index_id AS indexid,

partition_number AS partitionnum,

avg_fragmentation_in_percent AS frag

INTO work_to_do

FROM sys.dm_db_index_physical_stats (DB_ID(), NULL, NULL , NULL, 'LIMITED')

WHERE avg_fragmentation_in_percent > 10.0 AND index_id > 0;

DECLARE partitions CURSOR FOR SELECT * FROM work_to_do;

OPEN partitions;

FETCH NEXT

FROM partitions

INTO @objectid, @indexid, @partitionnum, @frag;

WHILE @@FETCH_STATUS = 0

BEGIN;

SELECT @objectname = o.name, @schemaname = s.name

FROM sys.objects AS o

JOIN sys.schemas as s ON s.schema_id = o.schema_id

WHERE o.object_id = @objectid;

SELECT @indexname = name

FROM sys.indexes

WHERE object_id = @objectid AND index_id = @indexid;

SELECT @partitioncount = count (*)

FROM sys.partitions

WHERE object_id = @objectid AND index_id = @indexid;

IF @frag < 30.0

BEGIN;

SELECT @command = 'ALTER INDEX ' + @indexname + ' ON ' + @schemaname + '.' + @objectname + ' REORGANIZE';

IF @partitioncount > 1

SELECT @command = @command + ' PARTITION=' + CONVERT (CHAR, @partitionnum);

EXEC (@command);

END;

IF @frag >= 30.0

BEGIN;

SELECT @command = 'ALTER INDEX ' + @indexname +' ON ' + @schemaname + '.' + @objectname + ' REBUILD';

IF @partitioncount > 1

SELECT @command = @command + ' PARTITION=' + CONVERT (CHAR, @partitionnum);

EXEC (@command);

END;

FETCH NEXT FROM partitions INTO @objectid, @indexid, @partitionnum, @frag;

END;

-- Close and deallocate the cursor.

CLOSE partitions;

DEALLOCATE partitions;

IF EXISTS (SELECT name FROM sys.objects WHERE name = 'work_to_do')

DROP TABLE work_to_do;