sql server不存在“过期存储过程”概念,所谓“过期”实为对统计信息陈旧、参数嗅探失准或数据分布剧变导致执行计划性能下降的误称;系统不标记有效期,也不支持批量重编译,必须逐个诊断并按需处理。

不能“批量重编译所有过期的存储过程”——SQL Server 没有“过期”概念,也没有自动标记或批量触发重编译的内置机制。 所谓“过期”,通常是误传或对统计信息更新、计划缓存失效等行为的模糊理解。真正需要干预的,是那些因数据分布剧变、参数嗅探失准或统计信息陈旧导致性能骤降的过程,而它们必须逐个诊断、按需处理。
为什么不存在“过期存储过程”这个东西
SQL Server 不会给存储过程打时间戳或设置有效期。所谓“过期”执行计划,本质是缓存中某个 plan_handle 对应的计划不再适配当前数据特征(比如表行数翻了10倍、某列值分布从均匀变成倾斜99%为空),但 SQL Server 不会主动告诉你“这个计划过期了”。它只会在下次执行时根据内部规则决定是否重编译——而这取决于是否触发了重编译条件(如统计信息更新、sp_recompile 调用、临时表行数变更超阈值等),不是靠“日期判断”。
- 系统视图
sys.dm_exec_procedure_stats里没有is_expired或类似字段 -
sys.procedures中的modify_date只反映 DDL 修改时间,和执行计划有效性无关 - 试图用
create_date 筛选“老过程”并批量重编译,纯属无效操作——新过程也可能因参数问题跑出烂计划,老过程可能十年如一日稳如泰山
真正该查的:哪些过程正在频繁重编译
高频重编译才是性能隐患信号。重点看 plan_generation_num > 1 且 execution_count 较高的过程,说明计划反复失效,背后大概率是参数嗅探或统计信息问题。
- 运行这个查询定位嫌疑对象:
SELECT OBJECT_NAME(object_id) AS proc_name, database_id, plan_generation_num, execution_count, last_execution_time FROM sys.dm_exec_procedure_stats WHERE plan_generation_num > 1 AND execution_count > 10 ORDER BY plan_generation_num DESC, execution_count DESC;
- 如果结果为空,说明没发生异常重编译,“批量重编译”需求本身就不成立
- 若返回若干过程,不要急着批量处理;先挑 top 1,用
SET STATISTICS XML ON查它的实际执行计划,确认“Parameter Compiled Value”是否严重偏离传入值
三种重编译方式的成本差异极大,不能混用
即使确认某个过程真有问题,也得按场景选最轻量的手段,而不是一刀切 WITH RECOMPILE。
-
只影响单次调用:用
EXEC uspFoo @x = 123 WITH RECOMPILE—— DBA 救急可用,绝不能写进应用代码 -
只重编译过程内某条毒查询:在语句末尾加
OPTION (RECOMPILE)—— 适合过程里有一处逻辑对参数极度敏感(如小范围查索引、大范围该走扫描),其余部分稳定 -
整个过程每次执行都重编译:创建或修改时加
WITH RECOMPILE—— CPU 开销大,仅适用于参数组合完全不可预测且调用频次极低的 BI 报表类过程
更稳妥的替代路径:先修统计信息,再试 OPTIMIZE FOR
90% 的“疑似过期”问题,根源不在过程本身,而在底层统计信息不准或缺失。盲目上 WITH RECOMPILE 是最常见误操作,会导致 CPU 暴涨、并发下降。
- 检查关键表统计信息是否陈旧:
DBCC SHOW_STATISTICS('Orders', 'IX_OrderDate'),关注Modification Counter是否远大于Rows Sampled * 0.2 - 对问题表手动更新:
UPDATE STATISTICS Orders IX_OrderDate WITH FULLSCAN - 若参数分布固定(如
@status IN ('Shipped', 'Pending')占 95%),优先加OPTIMIZE FOR (@status = 'Shipped'),比WITH RECOMPILE更轻、更可控
真正要动手批量操作时,唯一安全的做法是:导出所有待处理过程的 sp_recompile 语句 → 人工审核依赖关系和调用频率 → 分批在维护窗口执行。别信“一键清理过期计划”的脚本——那不是优化,是埋雷。










