SET STATISTICS IO ON和TIME ON在存储过程内不生效,因其仅对会话级后续批处理生效,而过程内部属嵌套批处理;需置于过程开头并配合PRINT记录输出,或改用sys.dm_exec_requests实时抓取、Extended Events捕获sp_statement_completed事件进行精准监控。

直接在存储过程里加 SET STATISTICS IO ON 和 SET STATISTICS TIME ON 不生效
因为这两个开关只对当前会话的**后续批处理语句**生效,而存储过程内部执行的语句属于“嵌套批处理”,默认不继承会话级统计开关。你看到的 IO 和 time 输出,往往是调用该存储过程的外层语句的汇总值,不是过程内部每条 SQL 的明细。
常见错误现象:在 SSMS 查询窗口里先执行 SET STATISTICS IO ON,再执行 EXEC YourProc,结果面板只显示一次 IO summary,无法定位到过程里哪一行 SELECT 或 UPDATE 拉高了逻辑读。
- 真正起作用的方式是把开关放在存储过程**内部最开头**(但注意不能在
CREATE PROC语句块之外) - 必须搭配
PRINT或临时表记录输出,因为存储过程中开启后产生的统计信息不会自动返回给客户端——SSMS 只在“外部批处理”中才展示统计面板 - 生产环境慎用:每个语句都会触发额外计算,且
STATISTICS TIME的 CPU 时间是线程时间(worker_time),不是挂钟时间,高并发下可能失真
用 sys.dm_exec_requests + sys.dm_exec_sql_text 实时抓正在跑的语句资源消耗
这是唯一能拿到“存储过程中当前正在执行的那条语句”的实时 CPU / IO / 耗时的方法,适用于排查卡住、慢得离谱的运行中过程。
关键点在于:它不依赖你提前设开关,而是直接从内存中捞当前活动请求的状态快照。
-
total_elapsed_time单位是微秒,注意除以 1000000 才是秒;5 秒以上可视为异常 -
cpu_time是该会话累计消耗的 CPU 微秒数,不是单条语句的,但结合statement_start_offset可定位到当前执行片段 -
logical_reads和writes是当前请求已发生的逻辑读写次数,反映 IO 压力 - 务必加
WHERE r.status = 'running',否则会混入已结束但尚未清理的记录
示例查询:
SELECT
r.session_id,
r.total_elapsed_time / 1000000.0 AS elapsed_sec,
r.cpu_time / 1000000.0 AS cpu_sec,
r.logical_reads,
r.writes,
SUBSTRING(t.text, (r.statement_start_offset/2) + 1,
(CASE WHEN r.statement_end_offset = -1
THEN LEN(CONVERT(NVARCHAR(MAX), t.text)) * 2
ELSE r.statement_end_offset END - r.statement_start_offset) / 2 + 1) AS current_statement
FROM sys.dm_exec_requests r
CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) t
WHERE r.session_id > 50 AND r.status = 'running' AND r.total_elapsed_time > 5000000;想长期监控又不干扰业务?别用 Profiler,改用 Extended Events 捕获 sp_statement_completed
Profiler 开销大、界面卡、日志爆炸,且 SQL Server 2019+ 已标记为“弃用”。真正轻量可控的是 Extended Events(XEvents)。
核心不是“抓所有语句”,而是精准过滤出你关心的部分——尤其是存储过程内部的语句级耗时。
- 事件选
sp_statement_completed(不是rpc_completed),它能捕获存储过程里每一条独立 SQL 的完成事件 - 必须加
WHERE [duration] > 1000000(即 >1 秒),否则会记录大量毫秒级语句,磁盘撑爆 - 加上
database_name = 'YourDB'和object_name = 'YourProc'过滤,避免跨库污染 - 导出后用 Excel 或 Power BI 按
sql_text分组,看哪类语句平均耗时最高、是否参数敏感
为什么不能只看执行时间?重点盯 sys.dm_exec_query_stats 里的缓存重用率
很多情况下,存储过程“变慢”不是代码问题,而是计划没重用:每次执行都重新编译,CPU 花在编译上,而不是执行上。
典型表现是:同一存储过程,不同参数值,total_worker_time 差 10 倍,但 execution_count 很低,plan_handle 却有几十个不同值。
- 查重用率:用
sys.dm_exec_query_stats关联sys.dm_exec_sql_text,按query_hash聚合,看相同逻辑的语句有多少 distinctplan_handle - 根本原因常是用了局部变量(如
DECLARE @id INT = @input_id后再WHERE id = @id),绕过了参数化,SQL Server 认为这是新语句 - 修复方式:直接用传入参数,或显式加
OPTION (RECOMPILE)(仅当参数值差异极大时)
真正难的不是抓数据,而是区分“是语句本身慢”,还是“慢在每次都在重新编译”。后者不会出现在 STATISTICS 或 XEvents 的 duration 里,只能靠缓存视图交叉验证。

















