SQL Server 2019 中 UPDATE 操作的 sys.dm_exec_requests.percent_complete 始终为 NULL,因其仅支持 BACKUP、RESTORE、DBCC 等少数命令;准确监控需用 OUTPUT 批次计数或 logical_reads 估算进度。

SQL Server 2019 不提供原生的“UPDATE 进度百分比”视图,sys.dm_exec_requests 中的 percent_complete 字段对普通 DML(包括 UPDATE)始终为 NULL——它只对备份、还原、DBCC 等少数命令有效。
为什么 sys.dm_exec_requests.percent_complete 是空的
这个字段是 SQL Server 内部为特定长时操作预留的进度钩子,UPDATE 不在支持列表里。你查到的 percent_complete = NULL 不是延迟或权限问题,而是设计如此。别浪费时间改权限或重连会话。
-
percent_complete只对BACKUP DATABASE、RESTORE DATABASE、DBCC CHECKDB、ALTER INDEX ... REBUILD等少数命令生效 - 即使加了
WITH (TABLOCK)或显式事务,UPDATE 依然不会填充该字段 - 试图用
WAITFOR DELAY配合轮询该字段,只会得到一串NULL
用 sys.dm_exec_sessions + logical_reads 估算已处理行数
大 UPDATE 通常按页扫描+逐行修改,logical_reads 和已处理数据量呈近似线性关系。结合表结构可反推进度。
- 先确认目标表平均行宽:用
sp_spaceused 'YourTable'查avg_record_size_in_bytes(若不可用,用SELECT AVG(DATALENGTH(...))估算) - 查当前会话读取页数:
SELECT session_id, logical_reads FROM sys.dm_exec_sessions WHERE session_id = @spid - 假设每页 8KB,一页约存
8192 / avg_record_size行 → 已扫行数 ≈logical_reads * (8192 / avg_record_size) - 注意:该估算含重复读(如锁升级导致重试)、索引维护开销,误差约 ±15%,但趋势可靠
捕获实际已提交的行数(需提前准备)
如果 UPDATE 是你发起的,且能接受轻微改造,最准的方式是拆成带计数的批次,并用 OUTPUT 记录中间状态。
- 不要写
UPDATE TOP (10000) ...后手动 sleep —— 缺少事务边界,易出错 - 改用循环批次 + 显式事务:
BEGIN TRAN; UPDATE TOP (5000) t SET status = 'done' FROM Orders t WHERE status = 'pending' OUTPUT inserted.order_id INTO #done_ids; SELECT @@ROWCOUNT AS 'this_batch'; COMMIT;
- 把
#done_ids行数累加到外部监控变量(如应用层计数器或临时表UPDATE progress SET cnt = cnt + @@ROWCOUNT) - 关键点:必须用
OUTPUT而非SELECT COUNT(*),否则无法区分“已更新但未提交”和“已提交”
避免用 DBCC OPENTRAN 或日志读取硬算进度
有人想从 DBCC OPENTRAN 查事务开始时间,再除以预估总耗时——这在 OLTP 场景下完全不可靠。
-
log_used增长不线性:UPDATE 带索引更新时,日志量取决于索引深度而非行数 - 事务日志 VLF 分布不均,
fn_dblog()解析开销大、易阻塞,且 SQL Server 2019 默认不启用READ_COMMITTED_SNAPSHOT时会锁日志读取 -
sys.dm_tran_database_transactions的database_transaction_log_bytes_used是累计值,无法映射到当前 UPDATE 的进度
真正实用的只有两个方向:用 logical_reads 看趋势,或用 OUTPUT 批次实测。后者精度高但需改写逻辑;前者零侵入但需理解估算偏差来源——比如触发器里没建索引的 JOIN,会让 logical_reads 突增,误判为“卡住”,其实是内部低效扫描。

















