找回密码
 立即注册
查看: 32|回复: 0

SQL Server 索引维护

[复制链接]

220

主题

53

回帖

3469

积分

管理员

积分
3469
发表于 2026-9-15 18:10:18 | 显示全部楼层 |阅读模式

一、先查:索引碎片率(第一步,先看哪些索引需要维护)

打开 SSMS,选中正航数据库,执行下面脚本,只看行数 > 1000 行的表(小表碎片不用管)



SELECT
    OBJECT_NAME(ips.object_id) AS 表名,
    i.name AS 索引名,
    ips.avg_fragmentation_in_percent AS 碎片率,
    ips.page_count AS 索引页数
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
INNER JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
WHERE ips.index_id > 0  --排除堆
  AND ips.page_count > 1000 --只看有价值的大索引
ORDER BY avg_fragmentation_in_percent DESC;

阈值规则(微软标准,ERP 推荐)

  • 碎片 <10%:不用处理
  • 碎片 10% ~ 30%:REORGANIZE【重组】,轻量、在线、不锁表
  • 碎片>30%:REBUILD【重建】,彻底清理碎片,开销大

二、重组 REORGANIZE(碎片 10~30%,推荐优先用这个,对业务影响小)

在线操作,几乎不锁表,适合 ERP 夜间维护



-- 单表全部索引重组(例如采购子表)
ALTER INDEX ALL ON dbo.采购单据子表 REORGANIZE;

-- 只重组某一个索引(前面我们建的采购联合索引)
ALTER INDEX idx_pur_trade_date_supp ON dbo.采购单据子表 REORGANIZE;

三、重建 REBUILD(碎片 > 30%,效果最强)

⚠️ 默认离线重建,会锁表!SQL Server 企业版支持 ONLINE=ON在线重建(不锁表);标准版没有 Online 选项,只能深夜没人的时候跑


--【企业版】在线重建(业务几乎不受影响)
ALTER INDEX ALL ON dbo.采购单据子表 REBUILD WITH(ONLINE=ON);

--【标准版/Express】离线重建,必须无人时段!
ALTER INDEX ALL ON dbo.采购单据子表 REBUILD;

--重建单个索引
ALTER INDEX idx_pur_trade_date_supp ON dbo.采购单据子表 REBUILD;

四、更新统计信息(非常关键!很多报表慢不是碎片,是统计过期)

SQL 查询优化器靠统计信息生成执行计划;单据持续新增后,统计信息过期,会选错执行计划,直接全表扫描,报表卡死无响应
-- 更新单表全量统计(采购子表,推荐!)
UPDATE STATISTICS dbo.采购单据子表 WITH FULLSCAN;

-- 整库全部表统计更新(低峰执行)
EXEC sp_updatestats;


`WITH FULLSCAN`:扫描全表生成统计,而不是抽样,**大 ERP 表推荐用这个,更精准**,代价是耗时更长。

五、一键自动脚本(推荐给客户定时作业,自动判断碎片执行维护)


这个脚本自动判断碎片,大于 30% 重建,10~30% 重组,最后更新统计,直接放到 SQL Server 代理定时作业,每晚自动跑



-- 索引自动维护脚本
DECLARE @sql NVARCHAR(MAX)='';
SELECT @sql +=
CASE
WHEN avg_fragmentation_in_percent>30 THEN
'ALTER INDEX '+QUOTENAME(i.name)+' ON '+QUOTENAME(OBJECT_SCHEMA_NAME(ips.object_id))+'.'+QUOTENAME(OBJECT_NAME(ips.object_id))+' REBUILD; '
WHEN avg_fragmentation_in_percent>=10 AND avg_fragmentation_in_percent<=30 THEN
'ALTER INDEX '+QUOTENAME(i.name)+' ON '+QUOTENAME(OBJECT_SCHEMA_NAME(ips.object_id))+'.'+QUOTENAME(OBJECT_NAME(ips.object_id))+' REORGANIZE; '
ELSE ''
END
FROM sys.dm_db_index_physical_stats(DB_ID(), NULL, NULL, NULL, 'LIMITED') ips
INNER JOIN sys.indexes i ON ips.object_id = i.object_id AND ips.index_id = i.index_id
WHERE ips.index_id>0 AND ips.page_count>1000;

EXEC sp_executesql @sql;
-- 全部表更新统计信息
EXEC sp_updatestats;
PRINT '索引维护+统计更新完成';

正航软件论坛 www.chixm.cn
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

手机版|小黑屋|CHIXM.CN ( 皖ICP备06002270号-5 )

GMT+8, 2026-9-26 13:25 , Processed in 0.054865 second(s), 20 queries .

Powered by Discuz! X5.0

© 2001-2026 CHIXM.CN FANS

快速回复 返回顶部 返回列表