WITH RECOMPILE 是应对极端参数分布且无法用统计信息或 OPTIMIZE FOR 解决的兜底手段;需先验证 plan_generation_num > 1、参数嗅探失准及排除统计信息过期等干扰,滥用反致性能恶化。

WITH RECOMPILE 不是“提速开关”,而是针对特定参数分布剧烈变化场景的兜底手段;滥用会导致 CPU 暴涨、执行变慢,必须先确认 plan_generation_num > 1 且参数嗅探失准才是根因。
怎么判断真需要 WITH RECOMPILE 而不是修统计信息或加 OPTIMIZE FOR
盲目加 WITH RECOMPILE 是最常见误操作。真正该用它的场景极少:只在某几个调用中参数值极端偏离(比如 @status = 'Cancelled' 占 0.02% 行,但该分支逻辑又必须走嵌套循环 JOIN),且无法拆分过程、也无法用 OPTIMIZE FOR 覆盖典型值时才考虑。
- 先查历史执行是否真重编译:
SELECT plan_generation_num, execution_count, last_execution_time 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,执行一次EXEC uspGetSalesInfoForDateRange '2025-01-01', '2025-01-31',看执行计划里是否出现 “Parameter Compiled Value” 和实际传入值严重不匹配 - 最后排除其他干扰:确认没混用
SET ARITHABORT ON/OFF,没在过程里拼EXEC(@sql),统计信息没过期(用DBCC SHOW_STATISTICS('Orders', 'IX_OrderDate')看Modification Counter是否远大于Rows Sampled * 0.2)
WITH RECOMPILE 的三种写法及适用边界
它不是万能药,三种写法成本差异极大,选错等于自废武功。
- 创建时加:
CREATE PROCEDURE uspReport @date DATE WITH RECOMPILE AS ...—— 整个过程每次执行都丢弃旧计划、全量重编译,仅适用于参数组合完全不可预测(如 BI 工具传来的任意日期范围 + 动态列名)、且调用频次极低( - 调用时加:
EXEC uspReport '2025-04-01' WITH RECOMPILE—— 只影响这一次执行,适合 DBA 手动救急,比如发现某次调用卡死,临时绕过烂计划,但不能写进应用代码 - 语句级加:
SELECT * FROM Orders WHERE OrderDate >= @from AND OrderDate —— 只重编译这一条 SELECT,其余逻辑仍用缓存计划,适合过程内存在一个“毒查询”(如小数据量用索引查找、大数据量该走扫描)而其他语句稳定的场景
比 WITH RECOMPILE 更轻、更稳的替代方案
90% 的“重编译慢”问题,其实靠更细粒度控制就能解决,根本不需要牺牲整个过程的计划缓存。
- 优先用
OPTIMIZE FOR (@param = 'Active'):在过程里对关键查询显式指定典型值,SQL Server 会按这个值生成计划,后续所有调用都复用——比每次重编译省 CPU,又比默认嗅探更可控 - 拆分支为独立过程:把
IF @mode = 'A' ... ELSE IF @mode = 'B'改成三个单独过程uspModeA、uspModeB、uspModeC,每个都有专属缓存计划,互不污染 - 强制更新统计信息:对高频查询的大表,定期跑
UPDATE STATISTICS Orders WITH FULLSCAN,比依赖自动更新更及时,避免因采样不准导致计划退化 - 统一 SET 选项:确保所有调用该过程的应用连接都设
SET ARITHABORT ON(或都 OFF),否则哪怕逻辑一模一样,SQL Server 也认为是两个不同上下文,各自编译
真正难的不是加不加 WITH RECOMPILE,而是分清“计划失效”“参数失准”“统计过期”“SET 冲突”这四类问题的信号特征;一旦混淆,加了也白加,还掩盖了真实瓶颈。

















