您现在的位置是:首页> ERP实施维护ERP实施维护

数据库碎片查询及优化

2026-01-31 ERP实施维护浏览量39

简介1:全库查询方法SELECT*FROM(SELECTSO.NAMEASobject_name,SI.NAMEASindex_name,IPS.avg_fragmentation_in_percent,(page_count*8.0/1024.0)ASsize_in_mB,si.type_descFROMsys.Dm_db_index_physical_stats(Db_id(),NULL,NULL

1:全库查询方法

SELECT *
FROM   (SELECT SO.NAME                       AS object_name,
               SI.NAME                       AS index_name,
               IPS.avg_fragmentation_in_percent,
               ( page_count * 8.0 / 1024.0 ) AS size_in_mB,
               si.type_desc
        FROM   sys.Dm_db_index_physical_stats(Db_id(), NULL, NULL, NULL, NULL) IPS
               INNER JOIN sys.indexes SI
                       ON SI.index_id = IPS.index_id
               INNER JOIN sys.objects SO
                       ON SO.object_id = SI.object_id
                          AND IPS.object_id = SO.object_id
        WHERE  alloc_unit_type_desc = 'IN_ROW_DATA'
               AND index_level = 0
               AND SI.NAME IS NOT NULL
               AND SO.is_ms_shipped = 0) t
ORDER  BY t.avg_fragmentation_in_percent DESC

2:单个表查询方法

SELECT SO.NAME                       AS object_name,
       SI.NAME                       AS index_name,
       IPS.avg_fragmentation_in_percent,
       ( page_count * 8.0 / 1024.0 ) AS size_in_mB,
       si.type_desc
FROM   sys.Dm_db_index_physical_stats(Db_id(), NULL, NULL, NULL, NULL) IPS
       INNER JOIN sys.indexes SI
               ON SI.index_id = IPS.index_id
       INNER JOIN sys.objects SO
               ON SO.object_id = SI.object_id
                  AND IPS.object_id = SO.object_id
WHERE  alloc_unit_type_desc = 'IN_ROW_DATA'
       AND index_level = 0
       AND SI.NAME IS NOT NULL
       AND SO.is_ms_shipped = 0
       AND so.name = 'T_META_OBJECTTYPE_L'--替换实际表名

2963ebbfd4fd0e7f47c77b09402c9e0f.docx

3:业务无使用的时候运行重建索引脚本

IF Object_id('tempdb..#temp_table_info') <> 0
  DROP TABLE #temp_table_info

IF Object_id('tempdb..#indexinfo') <> 0
  DROP TABLE #indexinfo

-- 创建临时表
CREATE TABLE #temp_table_info
  (
     TABLE_NAME SYSNAME
  );

CREATE TABLE #indexinfo
  (
     id              INT IDENTITY(1, 1),
     object_name     SYSNAME,
     fragmentpercent FLOAT,
     execsql         SYSNAME,
     objsize_mb      FLOAT
  );

go

SET nocount ON

DECLARE @record_log_flag BIT = 1 --记录索引重建优化的详细日志到表中,0表示false,1表示true
DECLARE @log_retention_time INT = 7 --日志保留时间,单位是天
DECLARE @i INT
DECLARE @icount INT
DECLARE @sql AS VARCHAR(max)
DECLARE @tbsize AS DECIMAL(19, 3)
DECLARE @driversize AS INT
DECLARE @logunusedsize INT=0
DECLARE @message VARCHAR(300)
DECLARE @tbname SYSNAME
DECLARE @begintime DATETIME
DECLARE @tmpbegintime DATETIME
DECLARE @tmpendtime DATETIME
DECLARE @tmpDurationInSeconds INT
DECLARE @worktime INT = 360
DECLARE @work_end_time DATETIME --任务截至时间
DECLARE @worktimeflag BIT
DECLARE @exectabs VARCHAR(max) = ''
DECLARE @noexectabs VARCHAR(max) = ''
DECLARE @checklogsize BIT=0
DECLARE @currenttablename VARCHAR(max)
DECLARE @delimiter CHAR(1) = ','
DECLARE @startindex INT = 1
DECLARE @fragmentpercent FLOAT

----------可人工调整的部分开始----------
--SET @worktime = 240 --允许脚本执行的工作时间,单位为分钟,默认为6个小时(0表示不限制工作时间,直至所有符合条件的索引重建完成)
--SET @noexectabs='T_BAS_OPERATELOGBK,T_BAS_OPERATELOGBK_S'--人工指定对某些表不进行重建索引,格式为't3,t4',默认为''(即不指定,对全库碎片率超过5的索引进行重建优化)
----------可人工调整的部分结束----------
DECLARE @noexectabslen INT = Len(@noexectabs);
DECLARE @exectabslen INT = Len(@exectabs);

IF @record_log_flag = 1
  BEGIN
      IF NOT EXISTS (SELECT *
                     FROM   sys.objects
                     WHERE  object_id = Object_id(N'[dbo].[KDIndexMaintenanceLog]')
                            AND type IN ( N'U' ))
        --创建日志表
        BEGIN
            CREATE TABLE [dbo].KDIndexMaintenanceLog
              (
                 ID                          INT IDENTITY(1, 1) PRIMARY KEY,-- 自增ID作为主键
                 [TableName]                 SYSNAME NOT NULL,
                 objsize_mb                  FLOAT NOT NULL,
                 [AvgFragmentationInPercent] [FLOAT] NOT NULL,
                 execsql                     [NVARCHAR](200),
                 [RebuildStartTime]          [DATETIME] NULL,
                 [RebuildEndTime]            [DATETIME] NULL,
                 [DurationInSeconds]         [INT] NULL
              );

            --创建索引
            CREATE NONCLUSTERED INDEX IX_KDIndexMaintenanceLog
              ON KDIndexMaintenanceLog (TableName, RebuildStartTime);
        END

      --清理过期日志
      DELETE FROM [dbo].KDIndexMaintenanceLog
      WHERE  [RebuildStartTime] < Dateadd(DAY, -@log_retention_time, Cast (Getdate() AS DATE))
  END

IF @worktime > 0
  BEGIN
      SET @work_end_time= Dateadd(MINUTE, @worktime, Getdate())
      SET @worktimeflag=1
  END
ELSE
  SET @worktimeflag=0

--判断sql server版本是否高于2008 r2
IF ( Cast(Serverproperty('ProductMajorVersion') AS INT) > 10
      OR ( Cast(Serverproperty('ProductMajorVersion') AS INT) = 10
           AND Cast(Serverproperty('ProductMinorVersion') AS INT) >= 50 ) )
  SET @checklogsize=1

IF ( @worktimeflag = 1
     AND @work_end_time <= Getdate() )
  BEGIN
      SET @message=N'停止执行。当前系统时间为:'
                   + CONVERT(VARCHAR(30), Getdate(), 121)
                   + N'超过了任务截至时间:' + @work_end_time

      RAISERROR(@message,0,1);

      RETURN
  END;

SET @message=N'开始获取需要优化的表'
             + CONVERT(VARCHAR(30), Getdate(), 121)

RAISERROR(@message,0,1);

IF Len(Ltrim(Rtrim(@exectabs))) > 0
  BEGIN
      SET @startindex=1

      WHILE @startindex <= @exectabslen
        BEGIN
            SET @currenttablename = Ltrim(Rtrim(Substring(@exectabs, @startindex, CASE
                                                                                    WHEN Charindex(@delimiter, @exectabs, @startindex) = 0 THEN 8000
                                                                                    ELSE Charindex(@delimiter, @exectabs, @startindex) - @startindex
                                                                                  END)));

            INSERT INTO #temp_table_info
                        (TABLE_NAME)
            VALUES      (@currenttablename);

            SET @startindex = CASE
                                WHEN Charindex(@delimiter, @exectabs, @startindex) = 0 THEN @exectabslen + 1
                                ELSE Charindex(@delimiter, @exectabs, @startindex)
                                     + 1
                              END;
        END;

      WITH t
           AS (SELECT SO.NAME                       AS object_name,
                      SI.NAME                       AS index_name,
                      IPS.avg_fragmentation_in_percent,
                      ( page_count * 8.0 / 1024.0 ) AS size_in_mB,
                      si.type_desc
               FROM   sys.Dm_db_index_physical_stats(Db_id(), NULL, NULL, NULL, NULL) IPS
                      INNER JOIN sys.indexes SI
                              ON SI.index_id = IPS.index_id
                      INNER JOIN sys.objects SO
                              ON SO.object_id = SI.object_id
                                 AND IPS.object_id = SO.object_id
               WHERE  alloc_unit_type_desc = 'IN_ROW_DATA'
                      AND index_level = 0
                      AND SI.NAME IS NOT NULL
                      AND SO.is_ms_shipped = 0
                      AND so.NAME NOT LIKE 'tmp%'
                      AND so.NAME NOT LIKE 'z%'
                      AND so.NAME NOT LIKE 'gle%'
                      AND so.NAME IN(SELECT TABLE_NAME
                                     FROM   #temp_table_info)),
           m
           AS (SELECT object_name,
                      Max(avg_fragmentation_in_percent) fragmentpercent,
                      CASE
                        WHEN Max(avg_fragmentation_in_percent) >= 15 THEN 'dbcc dbreindex(' + object_name
                                                                                                 + ') with no_infomsgs'
                        WHEN Max(avg_fragmentation_in_percent) >=  5 THEN 'dbcc indexdefrag(0,' + object_name
                                                                         + ') with no_infomsgs'
                        ELSE ''
                      END                               execsql,
                      Sum(size_in_mB)                   objsize_mb
               FROM   t
               WHERE  avg_fragmentation_in_percent > 0
               GROUP  BY object_name)
      INSERT INTO #indexinfo
      SELECT m.*
      FROM   m
      WHERE  m.execsql <> ''
      ORDER  BY 2 DESC
  END
ELSE IF Len(Ltrim(Rtrim(@noexectabs))) > 0
  BEGIN
      SET @startindex=1

      WHILE @startindex <= @noexectabslen
        BEGIN
            SET @currenttablename = Ltrim(Rtrim(Substring(@noexectabs, @startindex, CASE
                                                                                      WHEN Charindex(@delimiter, @noexectabs, @startindex) = 0 THEN 8000
                                                                                      ELSE Charindex(@delimiter, @noexectabs, @startindex) - @startindex
                                                                                    END)));

            INSERT INTO #temp_table_info
                        (TABLE_NAME)
            VALUES      (@currenttablename);

            SET @startindex = CASE
                                WHEN Charindex(@delimiter, @noexectabs, @startindex) = 0 THEN @noexectabslen + 1
                                ELSE Charindex(@delimiter, @noexectabs, @startindex)
                                     + 1
                              END;
        END;

      WITH t
           AS (SELECT SO.NAME                       AS object_name,
                      SI.NAME                       AS index_name,
                      IPS.avg_fragmentation_in_percent,
                      ( page_count * 8.0 / 1024.0 ) AS size_in_mB,
                      si.type_desc
               FROM   sys.Dm_db_index_physical_stats(Db_id(), NULL, NULL, NULL, NULL) IPS
                      INNER JOIN sys.indexes SI
                              ON SI.index_id = IPS.index_id
                      INNER JOIN sys.objects SO
                              ON SO.object_id = SI.object_id
                                 AND IPS.object_id = SO.object_id
               WHERE  alloc_unit_type_desc = 'IN_ROW_DATA'
                      AND index_level = 0
                      AND SI.NAME IS NOT NULL
                      AND SO.is_ms_shipped = 0
                      AND so.NAME NOT LIKE 'tmp%'
                      AND so.NAME NOT LIKE 'z%'
                      AND so.NAME NOT LIKE 'gle%'
                      AND so.NAME NOT IN(SELECT TABLE_NAME
                                         FROM   #temp_table_info)),
           m
           AS (SELECT object_name,
                      Max(avg_fragmentation_in_percent) fragmentpercent,
                      CASE
                        WHEN Max(avg_fragmentation_in_percent) >= 15 THEN 'dbcc dbreindex(' + object_name
                                                                                                 + ') with no_infomsgs'
                        WHEN Max(avg_fragmentation_in_percent) >= 5 THEN 'dbcc indexdefrag(0,' + object_name
                                                                         + ') with no_infomsgs'
                        ELSE ''
                      END                               execsql,
                      Sum(size_in_mB)                   objsize_mb
               FROM   t
               WHERE  avg_fragmentation_in_percent > 0
               GROUP  BY object_name)
      INSERT INTO #indexinfo
      SELECT m.*
      FROM   m
      WHERE  m.execsql <> ''
      ORDER  BY 2 DESC
  END
ELSE
  BEGIN
      WITH t
           AS (SELECT SO.NAME                       AS object_name,
                      SI.NAME                       AS index_name,
                      IPS.avg_fragmentation_in_percent,
                      ( page_count * 8.0 / 1024.0 ) AS size_in_mB,
                      si.type_desc
               FROM   sys.Dm_db_index_physical_stats(Db_id(), NULL, NULL, NULL, NULL) IPS
                      INNER JOIN sys.indexes SI
                              ON SI.index_id = IPS.index_id
                      INNER JOIN sys.objects SO
                              ON SO.object_id = SI.object_id
                                 AND IPS.object_id = SO.object_id
               WHERE  alloc_unit_type_desc = 'IN_ROW_DATA'
                      AND index_level = 0
                      AND SI.NAME IS NOT NULL
                      AND SO.is_ms_shipped = 0
                      AND so.NAME NOT LIKE 'tmp%'
                      AND so.NAME NOT LIKE 'z%'
                      AND so.NAME NOT LIKE 'gle%'),
           m
           AS (SELECT object_name,
                      Max(avg_fragmentation_in_percent) fragmentpercent,
                      CASE
                        WHEN Max(avg_fragmentation_in_percent) >= 15 THEN 'dbcc dbreindex(' + object_name
                                                                                                 + ') with no_infomsgs'
                        WHEN Max(avg_fragmentation_in_percent) >= 5 THEN 'dbcc indexdefrag(0,' + object_name
                                                                         + ') with no_infomsgs'
                        ELSE ''
                      END                               execsql,
                      Sum(size_in_mB)                   objsize_mb
               FROM   t
               WHERE  avg_fragmentation_in_percent > 0
               GROUP  BY object_name)
      INSERT INTO #indexinfo
      SELECT m.*
      FROM   m
      WHERE  m.execsql <> ''
      ORDER  BY 2 DESC
  END

SET @icount=(SELECT Count(1)
             FROM   #indexinfo);
SET @message=N'结束获取需要优化的表'
             + CONVERT(VARCHAR(30), Getdate(), 121)

RAISERROR(@message,0,1);

IF @icount = 0
  BEGIN
      SET @message=N'数据库:' + Db_name() + N'不需要优化'

      RAISERROR(@message,0,1);

      RETURN
  END;

SET @begintime=Getdate()
SET @message=N'数据库:' + Db_name() + N',开始时间: '
             + CONVERT(VARCHAR(30), @begintime, 121)
             + N' 总共需要执行:' + Cast(@icount AS VARCHAR)
             + N' 条语句'

RAISERROR(@message,0,1);

SET @i=1

WHILE @i <= @icount
  BEGIN
      SELECT @sql = execsql,
             @tbsize = objsize_mb,
             @tbname = object_name,
             @fragmentpercent = fragmentpercent
      FROM   #indexinfo
      WHERE  id = @i

      IF( @checklogsize = 1 )
        BEGIN
            SELECT @driversize = Cast(Cast(available_bytes AS DECIMAL) / ( 1024 * 1024 ) AS BIGINT)
            FROM   sys.master_files AS f
                   CROSS APPLY sys.Dm_os_volume_stats(f.database_id, f.file_id)
            WHERE  f.database_id = Db_id()
                   AND f.type_desc = 'LOG';

            SELECT @logunusedsize = ( total_log_size_in_bytes - used_log_space_in_bytes ) / 1024 / 1024
            FROM   sys.dm_db_log_space_usage

            IF @i = 1
              BEGIN
                  SET @message=N'日志可用空间为(MB):'
                               + Cast(@logunusedsize AS VARCHAR)
                               + N';磁盘可用空间为(MB):'
                               + Cast(@driversize AS VARCHAR)

                  RAISERROR(@message,0,1);
              END;
        END

      IF ( @worktimeflag = 1
           AND @work_end_time <= Getdate() )
        BEGIN
            SET @message=N'停止执行。因为当前系统时间为:'
                         + CONVERT(VARCHAR(30), Getdate(), 121)
                         + N'超过了开始工作时间范围'
                         + CONVERT(VARCHAR(30), @work_end_time, 121)
                         + N'不允许执行优化计划'

            RAISERROR(@message,0,1);

            RETURN
        END

      IF ( @tbsize > ( @driversize + @logunusedsize )
           AND @checklogsize = 1 )
        BEGIN
            SET @message=N'磁盘可用空间和日志可用空间不足,执行终止。原因:表:' + @tbname
                         + N'总需空间大小(MB):' + Cast(@tbsize AS VARCHAR)
                         + N';日志可用空间为(MB):'
                         + Cast(@logunusedsize AS VARCHAR)
                         + N';磁盘可用空间为(MB):'
                         + Cast(@driversize AS VARCHAR)

            RAISERROR(@message,16,1);

            RETURN
        END
      ELSE
        BEGIN
            SET @message= Cast(@i AS VARCHAR) + '/'
                          + Cast(@icount AS VARCHAR) + N'('
                          + Cast(@tbsize AS VARCHAR) + 'MB):' + @tbname
            SET @tmpbegintime=Getdate()

            IF @record_log_flag = 1
              BEGIN
                  --记录日志
                  INSERT INTO KDIndexMaintenanceLog
                              (TableName,
                               objsize_mb,
                               AvgFragmentationInPercent,
                               execsql,
                               RebuildStartTime)
                  VALUES      (@tbname,
                               @tbsize,
                               @fragmentpercent,
                               @sql,
                               @tmpbegintime)
              END

            EXEC(@sql)

            SET @tmpendtime=Getdate()
            SET @tmpDurationInSeconds = Datediff(second, @tmpbegintime, @tmpendtime)

            IF @record_log_flag = 1
              BEGIN
                  --更新日志表中索引优化完成时间
                  UPDATE KDIndexMaintenanceLog
                  SET    RebuildEndTime = @tmpendtime,
                         DurationInSeconds = @tmpDurationInSeconds
                  WHERE  TableName = @tbname
                         AND RebuildStartTime = @tmpbegintime;
              END

            SET @message=@message + N':'
                         + Cast (@tmpDurationInSeconds AS NVARCHAR)
                         + N'秒'

            RAISERROR(@message,0,1);
        END

      SET @i=@i + 1
  END

SET @message=N'数据库:' + Db_name() + N',结束时间: '
             + CONVERT(VARCHAR(30), Getdate(), 121)
             + N' 总耗时(秒):'
             + Cast(Datediff(ss, @begintime, Getdate()) AS VARCHAR)

RAISERROR(@message,0,1);

4:如果以上执行未完成,下次可以继续进行上次未完成的作业

如果部分未执行到可以单独手动执行,确保无业务使用的时候执行

dbcc dbreindex('T_CRM_CUSTOMER')--老版本SQL单表重建索引
-- 重建表的所有索引(离线,会锁表,适合维护窗口)
ALTER INDEX ALL ON T_CRM_CUSTOMER REBUILD;
GO
-- 在线重建索引(需企业版,不锁表,适合生产环境)
ALTER INDEX ALL ON T_CRM_CUSTOMER REBUILD WITH (ONLINE = ON, FILLFACTOR = 90);
GO
-- 仅重建指定索引(例如非聚集索引 IX_T_CRM_CUSTOMER_PHONE)
ALTER INDEX IX_T_CRM_CUSTOMER_PHONE ON T_CRM_CUSTOMER REBUILD;
GO

5:查询单表优化后的情况

-- 检查 T_CRM_CUSTOMER 表的所有索引碎片情况
DBCC SHOWCONTIG('T_CRM_CUSTOMER')
GO
-- 详细模式(输出更全的统计信息,推荐)
DBCC SHOWCONTIG('T_CRM_CUSTOMER') WITH ALL_INDEXES, TABLERESULTS
GO


Tags: #ERP #云星空 #金蝶云星空 #金蝶ERP

随机图文

文章评论

站点信息

  • 站点名称:厦门沐智云科技有限公司
  • 站点关键词:金蝶云,云星空,ERP系统,钉钉OA系统
  • 栏目数量58篇文章
  • 标签管理标签云
  • 微信公众号:扫描二维码,关注我们