调用sp_refreshsqlmodule报错:UNION等操作符需表达式数量匹配
时态表UNION场景下sp_refreshsqlmodule报错的原因解析
问题背景
存储过程中包含以下UNION查询:
SELECT *,SysStartTime, SysEndTime FROM dbo.FirstTable WHERE Id = @Id UNION SELECT * FROM history.FirstTable WHERE Id = @Id
其中dbo.FirstTable是SQL Server时态表,history.FirstTable是对应的历史表。执行刷新模块元数据的命令:
exec sp_refreshsqlmodule N'USP_MySPName'
时触发错误:
Msg 205, Level 16, State 1, Procedure sys.sp_refreshsqlmodule_internal, Line 85 [Batch Start Line 0]
All queries combined using a UNION, INTERSECT or EXCEPT operator must have an equal number of expressions in their target lists.
但修改存储过程、执行存储过程或单独运行该查询均无异常,手动指定所有列名而非使用*可修复此错误。
核心原因
时态表与历史表的
*解析差异- 时态表的
SysStartTime和SysEndTime是隐藏系统列,使用SELECT *查询时不会返回这两列,必须显式指定才会被包含到结果集中。 - 历史表的对应列(同样命名为
SysStartTime和SysEndTime)是普通列,使用SELECT *时会直接返回所有列(包括这两列)。
- 时态表的
动态执行与静态元数据刷新的解析逻辑不同
- 执行查询或存储过程时,SQL Server会实时解析表结构:
SELECT *,SysStartTime, SysEndTime FROM dbo.FirstTable实际返回的列数是「时态表的所有用户列」+「2个系统列」,与SELECT * FROM history.FirstTable返回的列数(用户列+2系统列)完全一致,因此不会报错。 sp_refreshsqlmodule刷新元数据时,采用静态解析规则:它不会识别时态表隐藏列的特殊处理,会把SELECT *,SysStartTime, SysEndTime解析为「时态表的所有列(包括隐藏的系统列)+额外两个系统列」,导致该查询的列数比历史表的SELECT *多2列,触发UNION列数不匹配的错误。
- 执行查询或存储过程时,SQL Server会实时解析表结构:
解决方案
避免使用*通配符,显式指定所有需要查询的列名,确保UNION两边的列数和列顺序完全一致。示例如下:
SELECT Col1, Col2, SysStartTime, SysEndTime FROM dbo.FirstTable WHERE Id = @Id UNION SELECT Col1, Col2, SysStartTime, SysEndTime FROM history.FirstTable WHERE Id = @Id
内容的提问来源于stack exchange,提问作者Shardul
相关产品推荐
相关产品推荐

