使用CASE表达式检测列存在性时触发‘无效列名’错误的解决方法
问题描述
我正在构建一个每日运行的存储过程,由于数据提供方式的问题,部分日期导入到临时表的文件中缺少输出所需的部分列。要求输出中对应的列必须存在,值可以为null或空字符串。
我尝试了以下代码:
select case when exists( select * from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME = 'tableName' and COLUMN_NAME = 'ABC' ) then ABC else '' end as 'XYZ' from tableName
测试时明确知道列ABC不存在,期望该SELECT语句为列XYZ返回空字符串,但运行时报错:
Invalid column name 'ABC'.
原本以为EXISTS(...)会判定为FALSE并直接执行ELSE分支,但似乎列名仍会被解析,请问如何解决这个问题?
解决方案
SQL的编译阶段会优先解析所有引用的列名,不管CASE分支的条件是否成立,只要代码里写了ABC,编译时就会检查这个列是否存在,所以哪怕EXISTS条件为假,还是会触发报错。
解决这个问题必须用动态SQL,因为动态SQL是在运行时才编译执行的,可以根据列是否存在来拼接不同的查询语句:
DECLARE @sql NVARCHAR(MAX) IF EXISTS(SELECT * FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'tableName' AND COLUMN_NAME = 'ABC') BEGIN SET @sql = N'SELECT ABC AS XYZ FROM tableName' END ELSE BEGIN SET @sql = N'SELECT '''' AS XYZ FROM tableName' END EXEC sp_executesql @sql
如果需要处理多个列,只需扩展这个逻辑,逐个检查列是否存在后拼接对应的SELECT字段即可。
另外注意:如果是临时表(比如#tableName),INFORMATION_SCHEMA.COLUMNS无法查询到临时表的结构,此时要改用tempdb的系统表来检查:
DECLARE @sql NVARCHAR(MAX) IF EXISTS(SELECT * FROM tempdb.sys.columns WHERE object_id = OBJECT_ID('tempdb..#tableName') AND name = 'ABC') BEGIN SET @sql = N'SELECT ABC AS XYZ FROM #tableName' END ELSE BEGIN SET @sql = N'SELECT '''' AS XYZ FROM #tableName' END EXEC sp_executesql @sql
内容的提问来源于stack exchange,提问作者pontedm
相关产品推荐
相关产品推荐

