sp_MSforeachtable无法批量重新编译存储过程,因其仅遍历sys.tables中type='U'的用户表,而存储过程属于sys.procedures中type='P'的对象,二者完全不匹配。

不能直接用 sp_MSforeachtable 批量重新编译存储过程 —— 它只遍历表(U 类型对象),而存储过程是 P 类型,压根不匹配。
为什么 sp_MSforeachtable 对存储过程完全无效
sp_MSforeachtable 的设计目标就是遍历当前数据库中所有用户表(sys.tables),内部查询固定过滤 type = 'U'。它不识别视图、函数、存储过程或触发器。哪怕你强行传入 @whereand = "and type = 'P'",也不会生效——该参数只作用于 sys.tables 视图,而存储过程不在这个视图里。
常见错误现象:exec sp_MSforeachtable @command1="sp_recompile '?'" 看似执行成功,但实际什么都没做,因为 ? 替换的全是表名,不是存储过程名;对表执行 sp_recompile 虽不报错,但无意义(表本身不缓存执行计划)。
- 它只操作
sys.tables,和sys.procedures无关 -
@replacechar只能替代表名,不能替换成存储过程名 - 没有
@objecttype或类似机制来切换遍历目标
真正能批量重编译存储过程的替代方案
必须改用能枚举存储过程的对象遍历方式。最稳妥的是手写游标或用 sys.procedures 配合动态 SQL:
DECLARE @proc_name NVARCHAR(256)
DECLARE proc_cursor CURSOR FOR
SELECT QUOTENAME(SCHEMA_NAME(schema_id)) + '.' + QUOTENAME(name)
FROM sys.procedures
WHERE is_ms_shipped = 0 -- 排除系统内置过程
<p>OPEN proc_cursor
FETCH NEXT FROM proc_cursor INTO @proc_name</p><p>WHILE @@FETCH_STATUS = 0
BEGIN
PRINT 'Recompiling ' + @proc_name
EXEC sp_recompile @proc_name
FETCH NEXT FROM proc_cursor INTO @proc_name
END</p><p>CLOSE proc_cursor
DEALLOCATE proc_cursor- 用
sys.procedures确保只查存储过程,is_ms_shipped = 0避免触碰系统过程 -
QUOTENAME防止过程名含特殊字符或空格导致语法错误 -
sp_recompile参数必须是完整三段式名称(如[dbo].[usp_GetOrders]),否则可能失败
如果坚持要用“foreach”类工具:自定义 sp_MSforeachObject
知识库中提到的 sp_MSforeachObject(非官方但广泛流传)支持按 @objectType 过滤,可指定 'P' 遍历存储过程:
EXEC sp_MSforeachObject
@objectType = 'P',
@command1 = 'PRINT ''?''; EXEC sp_recompile ''?'';'但注意:
- 该存储过程不在 SQL Server 默认安装中,需手动创建(参考知识库中
CREATE proc sp_MSforeachObject的定义) - 不同版本间参数行为可能不一致,比如某些实现不支持
QUOTENAME自动包裹,仍需在@command1中手动加引号 - Azure SQL Database 和 Azure Synapse 不支持
sp_recompile,此方案在云环境会直接失败
重编译前必须确认的三个前提
盲目批量重编译可能引发性能抖动甚至阻塞。务必检查:
- 是否已排除系统数据库(
master、model、msdb)?对它们执行sp_recompile通常没必要且有风险 - 是否确认这些存储过程近期经历了数据分布突变、索引新增/删除、统计信息过期?否则重编译不会带来收益
- 是否避开业务高峰?因为
sp_recompile会清空缓存中的旧计划,首次调用时需重新编译,延迟明显升高
最常被忽略的一点:重编译只是让下次执行时生成新计划,它不立即执行过程,也不保证新计划一定更优——最终效果取决于参数值分布和当前数据状态。

















