sql server中存储过程性能取决于参数化方式、执行计划复用及参数嗅探,而非create procedure语法本身;参数类型不匹配引发隐式转换致索引失效,动态sql需用sp_executesql并避免对象名拼接,with recompile应慎用。

直接说结论:SQL Server里带参数的CREATE PROCEDURE本身不决定性能,真正影响效率的是参数化方式、执行计划复用能力,以及是否触发参数嗅探异常。
为什么@param类型选错会导致存储过程变慢
SQL Server对不同数据类型的参数会生成不同执行计划,尤其是varchar和nvarchar混用时容易隐式转换,导致索引失效。
- 如果调用方传
nvarchar,但存储过程定义为varchar,SQL Server会在列上加CONVERT_IMPLICIT,跳过索引查找 -
datetime和datetime2混用同理,可能让优化器放弃使用已有的统计信息 - 用
sql_variant或max类型(如varchar(max))作参数,常导致“乐观编译”失败,回退到低效计划
如何避免EXEC sp_executesql绕过缓存的问题
动态拼接SQL时,哪怕只差一个空格,SQL Server也会视为新语句,单独编译并缓存——这会快速耗尽计划缓存,还拖慢首次执行。
- 始终用
sp_executesql代替EXEC(),它支持参数化,能复用计划 - 参数名必须完全一致(包括大小写),例如
@id和@ID是两个不同参数 - 避免在
sp_executesql中拼接表名或列名;真需要动态对象名,改用sys.sp_executesql配合QUOTENAME()防注入,但要接受该部分无法缓存
WITH RECOMPILE不是性能救星,而是临时止痛药
加这个选项会让每次执行都重新编译,看似避开“参数嗅探”旧计划问题,实则代价更高——CPU压力翻倍,且失去所有计划重用收益。
- 只在极少数场景适用:参数值分布极度倾斜(如99%查
status = 1,1%查status = 999),且无法拆分逻辑 - 更稳妥的做法是用
OPTION (RECOMPILE)仅标记具体查询,而非整个存储过程 - SQL Server 2016+ 可开启
QUERY_OPTIMIZER_HOTFIXES或使用USE HINT('ASSUME_MIN_SELECTIVITY_FOR_AWESOME_ESTIMATES')(玩笑,别真用),实际应优先考虑OPTIMIZE FOR或OPTIMIZE FOR UNKNOWN
参数嗅探不是bug,是优化器基于首次参数值做估算的正常行为;真正难处理的是业务逻辑把“查全部”和“查单条”塞进同一个存储过程——这种设计比语法细节更容易拖垮性能。











