最省事且结果最可控的方式是调用sp_benchmarktsql存储过程,需显式传入exec语句、慎用@clearcache=1、设@numberofexecution为5~10并剔除首行取平均,或配合set statistics time on获取引擎级elapsed time,同时注意清理对应计划缓存以确保多版本对比准确。

直接用 sp_BenchmarkTSQL 测单个存储过程执行耗时
最省事、结果最可控的方式,就是调用 sqlserver-kit 提供的 sp_BenchmarkTSQL 存储过程。它专为重复执行 + 时间统计设计,比手动写 GETDATE() 或客户端计时靠谱得多。
- 必须显式传入
@tsqlStatement,例如'EXEC YourProc @Param = 123'—— 不能只写'YourProc',否则会报错或测不到实际执行逻辑 -
@clearCache = 1要慎用:清缓存(DBCC DROPCLEANBUFFERS; DBCC FREEPROCCACHE;)会让每次执行都走硬解析+物理读,测的是“冷启动最差情况”,不是常态性能;日常对比建议设为0 -
@numberOfExecution推荐设为5~10:太少波动大,太多可能掩盖首次编译开销;观察输出里的Elapsed time (ms)列,剔除首行(含编译),取后几轮平均值 - 结果默认只打印到消息窗口;如需存档分析,加
@saveResults = 1,它会写进dbo.BenchmarkResults表,字段含min_elapsed、max_elapsed、avg_logical_reads
用 SET STATISTICS TIME ON 捕获真实引擎耗时
这是 SQL Server 内部最权威的单次耗时来源,绕过网络、驱动、客户端渲染延迟,只反映 SQL Server 引擎本身的 Elapsed time。
- 每次测试前必须重置:
SET STATISTICS TIME ON;→ 执行存储过程 →SET STATISTICS TIME OFF;。漏掉OFF会导致后续所有语句都输出统计,污染结果 - 绝对不要勾选 SSMS 的“包含实际执行计划”:它强制生成并传输 XML 执行计划,会额外增加 20–200ms 开销,且干扰参数嗅探行为
- 关注输出中
SQL Server Execution Times:下的Elapsed time = xxx ms,忽略Parse and Compile Time—— 编译开销属于一次性成本,不应计入常规性能指标 - 首次执行必然含编译,建议先单独跑一次预热,再连续执行 3 次取
Elapsed time平均值
对比多个存储过程版本时,必须清理对应计划缓存
不同版本的存储过程如果共享同一个 plan_handle,SQL Server 可能复用旧版编译计划,导致新版优化没生效,测出来反而更慢——这是多版本对比中最常见的误判根源。
- 切换版本后,立刻执行:
DBCC FREEPROCCACHE (<code>plan_handle),其中plan_handle从sys.dm_exec_cached_plans中查,过滤条件是objtype = 'Proc'且OBJECT_NAME(plan_handle) = 'YourProcName' - 临时方案可用
ALTER PROCEDURE YourProcName WITH RECOMPILE,但仅限测试阶段;长期开启会丢失“计划稳定性”这一关键维度,掩盖参数嗅探问题 - 避免用
DBCC FREEPROCCACHE不带参数:它清空整个缓存,影响其他正在运行的查询,测试环境也应精准定位 - 验证是否清干净:执行后查
sys.dm_exec_query_stats,确认目标过程的execution_count是否归零
生产环境回溯对比,靠 sys.dm_exec_query_stats 提取历史指标
上线后的版本效果不能重跑,只能从缓存中挖数据。但直接查 sys.dm_exec_query_stats 容易混入无关语句,尤其当多个存储过程名字相似或 SQL 文本雷同。
- 必须关联
sys.dm_exec_sql_text(plan_handle),用text字段确认是否真出自目标存储过程,不能只靠object_name(plan_handle)—— 重命名或改名后该函数返回 NULL - 关键指标只看两个:
total_elapsed_time / execution_count(平均耗时)、total_logical_reads / execution_count(平均逻辑读);CPU 时间受并发干扰大,参考价值低于 I/O 指标 - 时间范围要限定:加
last_execution_time >= DATEADD(hour, -24, GETDATE()),避免历史垃圾计划拖累平均值 - 注意权限:需要
VIEW SERVER STATE权限才能查这些 DMV,普通账号常被拒,提前配好测试账号
EXEC 调用背后,SQL Server 已经悄悄复用了三天前的旧计划。











