应查 sys.sql_modules.definition 并清洗换行符后用 EXCEPT 比对,同时需校验 EXECUTE AS、ENCRYPTION、SET 选项等元数据,因 OBJECT_DEFINITION() 跨库失效且含不可见字符。

直接查 sys.sql_modules 获取原始定义
存储过程的完整脚本(不含注释、格式化)存在 sys.sql_modules 的 definition 字段里,不是 sys.procedures。只比对 name 和 object_id 只能发现“有没有”,不能看出“哪里不同”。
实操建议:
• 必须用 JOIN sys.procedures 过滤掉触发器、函数等干扰对象
• 对 definition 做 REPLACE(REPLACE(..., CHAR(13), ''), CHAR(10), '') 清除换行符,否则 EXCEPT 会因空白差异误报
• 若目标库启用了加密(is_encrypted = 1),definition 返回 NULL,此时无法比对,得先解密或换用 Redgate SQL Compare
用 EXCEPT 找出定义文本差异
EXCEPT 是最轻量、最可控的差集比对方式,比字符串 LIKE 或 CONTAINS 更准,也比拼接 DDL 脚本更稳定。
实操建议:
• 分别从源库和目标库查出清洗后的 definition,插入两个临时表(如 #src_def 和 #dst_def)
• 执行 SELECT definition FROM #src_def EXCEPT SELECT definition FROM #dst_def,得到源有而目标无的语句片段
• 再反向执行一次,得到目标有而源无的部分
• 注意:EXCEPT 自动去重且忽略顺序,若需定位到某一行差异,得先按行拆分(用 STRING_SPLIT + 行号),但多数场景只需知道“整体不一致”即可
为什么不能只比 OBJECT_DEFINITION()?
OBJECT_DEFINITION() 看起来方便,但它在跨库调用时行为不稳定:
• 在目标库上下文中执行 OBJECT_DEFINITION(OBJECT_ID('usp_GetUser')),只能查本库对象,无法直接查另一个库的同名过程
• 若用四部分命名(如 Database2.sys.OBJECT_DEFINITION(...))会报错,因为该函数不支持跨库解析
• 它返回的文本可能含不可见字符(如 BOM 头),导致哈希校验失败
实操建议:
• 放弃 OBJECT_DEFINITION(),坚持走 sys.sql_modules + 库名前缀查询
• 示例写法:SELECT m.definition FROM Database1.sys.sql_modules m JOIN Database1.sys.procedures p ON m.object_id = p.object_id WHERE p.name = 'usp_GetUser'
加密对象、权限、SET 选项这些容易被忽略
即使 definition 文本完全一样,两个存储过程仍可能行为不同:
• EXECUTE AS 子句是否一致(影响执行上下文权限)
• WITH ENCRYPTION 是否开启(决定能否读取定义)
• WITH RECOMPILE 或 WITH EXECUTE AS 等选项是否匹配
• SET 选项如 ANSI_NULLS、QUOTED_IDENTIFIER 在创建时是否一致——这会影响后续查询计划缓存和语义(比如 NULL = NULL 判断)
实操建议:
• 把 uses_ansi_nulls、uses_quoted_identifier、execute_as_principal_id 等字段一并查出来比对
• 这些值在 sys.sql_modules 里都有对应列,漏掉就等于没真正比完

















