with recompile 不是性能开关而是高风险兜底手段,仅适用于参数极端偏离且无法用统计信息更新、optimize for 或局部变量解决的极少数场景,盲目使用反而降低性能。

不该盲目加 WITH RECOMPILE,它不是性能开关,而是高风险兜底手段——95% 的场景下,加了反而更慢。
怎么判断真需要 WITH RECOMPILE 而不是修统计信息或加 OPTIMIZE FOR
盲目加 WITH RECOMPILE 是最常见误操作。真正该用它的场景极少:
- 只在某几个调用中参数值极端偏离(比如
@status = 'Cancelled'占全表 0.02% 行,但该分支逻辑又必须走嵌套循环 JOIN) - 无法拆分过程、也无法用
OPTIMIZE FOR覆盖典型值 - 已确认不是统计信息过期、不是
SET ARITHABORT不一致、不是拼接动态 SQL 导致的计划缓存污染
验证步骤必须按顺序做:
- 查历史是否真重编译:
SELECT plan_generation_num, execution_count FROM sys.dm_exec_query_stats qs CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) st WHERE st.text LIKE '%uspGetSalesInfoForDateRange%'——plan_generation_num > 1才说明发生过重编译,但不等于该加WITH RECOMPILE - 确认是参数嗅探问题:在过程开头加
SET STATISTICS XML ON,执行一次,看执行计划里Parameter Compiled Value和实际传入值是否严重不匹配 - 排除干扰:用
DBCC SHOW_STATISTICS('Orders', 'IX_OrderDate')看Modification Counter是否远大于Rows Sampled * 0.2;检查有没有混用SET ARITHABORT ON/OFF
WITH RECOMPILE 的三种写法及成本差异
选错写法等于自废武功,三种方式 CPU 开销和适用边界完全不同:
-
创建时加:
CREATE PROCEDURE uspReport @date DATE WITH RECOMPILE AS ...—— 每次执行都丢弃旧计划、全量重编译,仅适用于参数组合完全不可预测(如 BI 工具传来的任意日期范围 + 动态列名)、且调用频次极低(每天 ≤ 5 次) -
调用时加:
EXEC uspReport '2025-04-01' WITH RECOMPILE—— 只影响这一次执行,适合 DBA 手动救急,绝不能写进应用代码 -
语句级加:
SELECT * FROM Orders WHERE OrderDate >= @from OPTION (RECOMPILE)—— 只重编译这一条语句,其余逻辑仍用缓存计划,适合过程内存在一个“毒查询”而其他语句稳定的场景
比 WITH RECOMPILE 更轻、更稳的替代方案
绝大多数参数嗅探问题,WITH RECOMPILE 都不是第一选择:
- 用局部变量隔离参数:
DECLARE @local_date DATE = @date; SELECT ... WHERE OrderDate = @local_date,让优化器失去参数值线索,转而依赖统计分布 - 用
OPTION (OPTIMIZE FOR (@date = '2025-01-01'))固定编译值,适合有典型业务日期的报表过程 - 用
OPTION (OPTIMIZE FOR (@status UNKNOWN))让优化器按平均分布估算,适合状态值极度倾斜但无典型值的场景 - 定期更新统计信息:
UPDATE STATISTICS Orders WITH FULLSCAN, NORECOMPUTE,尤其对高频更新的大表
真正棘手的是那种“每次传入值都导致执行路径彻底切换”的过程——比如一个过程既查单条订单详情(走索引查找),又查全量销售汇总(该走扫描),且无法拆分。这种才值得考虑语句级 OPTION (RECOMPILE),而不是整个过程加 WITH RECOMPILE。











