参数嗅探是sql server默认行为,变慢源于缓存计划复用错误;需通过sys.dm_exec_query_stats和sys.dm_exec_text_query_plan交叉验证parametercompiledvalue与parameterruntimevalue差异,并检查sys.dm_exec_requests确认阻塞类型。

参数嗅探不是 bug,是 SQL Server 的默认行为;变慢是因为缓存计划复用错了,不是计划本身坏了。
怎么确认真是参数嗅探在作怪
别靠“感觉”,查系统视图交叉验证:
- 用
sys.dm_exec_query_stats找出该存储过程的plan_handle,再用sys.dm_exec_text_query_plan(plan_handle, NULL, NULL)提取 XML 执行计划 - 在 XML 里搜索
<parameterlist></parameterlist>,对比ParameterCompiledValue(编译时值)和ParameterRuntimeValue(运行时值)是否差距极大——比如编译时是'Active'(占 95% 行),运行时是'Archived'(占 0.2% 行) - 查
sys.dm_exec_requests中当前慢请求的statement_text,确认它确实卡在某条带参数的 SELECT/UPDATE 上,而非锁或 I/O 等待
局部变量赋值必须写对才有效
写错等于没写,常见失效点:
- 声明和赋值必须在同一行:
DECLARE @status_local INT = @status;—— 分成DECLARE @status_local INT; SET @status_local = @status;可能被优化器绕过 - 类型和长度必须严格一致:
@status是INT,就不能声明成TINYINT;是NVARCHAR(100),就不能写成NVARCHAR(50),否则隐式转换让索引失效 - WHERE 中所有涉及参数的地方都得换:如果条件是
WHERE Company = @Company AND Region = @Region,两个都得用局部变量,漏一个就可能被穿透
OPTION (RECOMPILE) 加在哪、为什么不能加在开头
它只作用于紧邻的语句,不是整个存储过程:
- 错误写法:
ALTER PROCEDURE p AS BEGIN OPTION (RECOMPILE); SELECT * FROM t WHERE c = @p; END——OPTION不在语句末尾,语法报错或被忽略 - 正确写法:
SELECT * FROM t WHERE c = @p OPTION (RECOMPILE);—— 只重编译这一句,不影响其他逻辑 - 适用场景有限:适合低频调用、参数跨度极大(如时间范围从 1 天到 5 年)、且单次执行耗时远高于编译开销的报表类语句;高频 OLTP 场景下 CPU 压力会明显上升
PSP(参数敏感计划)不是自动开启的开关
SQL Server 2022 的 PSP 功能有硬性门槛,不满足就退化为单计划:
- 统计直方图必须非均匀:用
DBCC SHOW_STATISTICS('t', 'ix_c')查看Histogram步骤数 ≥ 200,且AVG_RANGE_ROWS差异明显(例如从 1 跳到 5000) - 最多只对前三个谓词做 PSP 评估:
WHERE a = @a AND b = @b AND c = @c AND d = @d,第四个@d不参与分流 - 数据库兼容级别必须 ≥ 160,且查询存储(
QUERY_STORE)必须处于READ_WRITE模式 - 混用字面量直接失效:
WHERE Company = @Company AND Status = 'Active'—— 后者破坏谓词独立性,PSP 不识别
真正容易被忽略的是:局部变量写法和 PSP 并不互斥,DECLARE @x = @x_in 后再参与 WHERE,PSP 仍能基于 @x 的分布生成多个计划;但如果你连统计信息都没更新过,或者直方图只有 3 步,那 PSP 根本不会触发。










