CASE WHEN T-SQL 中的错误结果表达式无效

Invalid false result expression in CASE WHEN T-SQL

我正在编写一个查询 table。问题是基于映射文件,我想将值作为常量或变量输入。

例如 - 如果在映射文件中 ExtDate 是常量,例如ExtDate = '2017-12-31' 我想使用这个值 (2017-12-31)。但是,如果 ExtDate 以 'Var' 开头,我想使用变量值 - 例如当 ExtDate = VarOpenDate 时,我想用 OpenDate 列中的值来填充该列。下面来自 MappingFile 的示例行:

CREATE TABLE MappingFile (ColNum INT, Variable CHAR(50), IsUsed CHAR(50), ID CHAR(50), ExtDate CHAR(50), DataDate CHAR(50), ValueDate CHAR(50), Flag CHAR(50), Unit CHAR(50))
INSERT INTO MappingFile VALUES (1,  'ClientId', 'YES', 'VarAcctID', '2017-12-31', 'VarSigningDate', 'VarSigningDate', 'X', '')  
INSERT INTO MappingFile VALUES (2,  'ProductGroup', 'YES', 'VarAcctID', 'VarOpenDate', 'VarSigningDate', 'VarSigningDate', 'X', '')     
INSERT INTO MappingFile VALUES (3,  'ProductType', 'YES', 'VarAcctID',  'VarOpenDate', 'VarSigningDate', 'VarSigningDate', 'X', '')     

为了做到这一点,我在下面写了一个代码(这是一个简化,因为有更多的列并且整个查询都在插入 while 循环中)。

DECLARE @I INT = 2
DECLARE @COL CHAR(50)
DECLARE @ID CHAR(50)
DECLARE @EDT CHAR(50)
DECLARE @DDT CHAR(50)
DECLARE @DTS CHAR(50) = 'dataset_name'

SET @ID  = (SELECT ID        FROM MappingFile WHERE ColNum = @I)
SET @EDT = (SELECT ExtDate   FROM MappingFile WHERE ColNum = @I)
SET @DDT = (SELECT DataDate  FROM MappingFile WHERE ColNum = @I)
SET @COL = (SELECT Variable  FROM MappingFile WHERE ColNum = @I)

EXEC('
        SELECT ''' + @DTS + ''',
                CASE WHEN SUBSTRING(''' + @ID  + ''', 1, 3) = ''Var'' THEN ' + @ID  + ' ELSE ''' + @ID  + ''' END,
                CASE WHEN SUBSTRING(''' + @EDT + ''', 1, 3) = ''Var'' THEN ' + @EDT + ' ELSE ''' + @EDT + ''' END,
                CASE WHEN SUBSTRING(''' + @DDT + ''', 1, 3) = ''Var'' THEN ' + @DDT + ' ELSE ''' + @DDT + ''' END,
                ''' + @COL + ''',
                ' + @COL + '        
FROM ' + @DTS + '
        WHERE ' + @COL + ' IS NOT NULL
        ')

不幸的是,如果 ExtDate 是常量字符串,查询会给我一个错误。这是由于结果表达式:

THEN ' + @EDT + '

returns 不是列名称的字符串。这给了我一个错误,尽管它不应该因为 if

SUBSTRING(''' + @ID  + ''', 1, 3) <> ''Var''

那么案件的结果就是

' ELSE ''' + @DDT + '''

这不是列名,而是常量字符串。

您动态构建了一个无法解析的查询。

考虑下面的测试查询:

DECLARE @T TABLE (
    Col int
    );

INSERT INTO @T VALUES (1);

SELECT CASE 
 WHEN 1=0 THEN NoSuch ELSE 'Hi' END
 FROM @T;

运行 这个,结果是

Msg 207, Level 16, State 1, Line 8 Invalid column name 'NoSuch'.

因为即使 CASE 不执行 THEN 并跳到 ELSE,解析器也不会提前知道并检查以确保 NoSuch 是一个专栏。如果不是,它会在尝试 运行 之前拒绝查询。

一种解决方案是检查动态字符串外部的变量。

感谢@Tab Alleman,我设法找到了解决方案。解析器的现象确实如此,因此我不得不检查EXEC命令之外的变量。

下面找到有效的代码:

DECLARE @I INT = 2
DECLARE @COL CHAR(50)
DECLARE @ID CHAR(50)
DECLARE @EDT CHAR(50)
DECLARE @DDT CHAR(50)
DECLARE @DTS CHAR(50) = 'dataset_name'

SET @ID  = (SELECT CASE WHEN SUBSTRING(ID       , 1, 3) = 'Var' THEN LTRIM(RTRIM(RIGHT(ID       , LEN(ID        ) - 3))) WHEN ID            = '' THEN 'NULL' ELSE CONCAT('''', LTRIM(RTRIM(ID       )), '''') END FROM MappingFile WHERE ColNum = @I)
SET @EDT = (SELECT CASE WHEN SUBSTRING(ExtDate  , 1, 3) = 'Var' THEN LTRIM(RTRIM(RIGHT(ExtDate  , LEN(ExtDate   ) - 3))) WHEN ExtDate       = '' THEN 'NULL' ELSE CONCAT('''', LTRIM(RTRIM(ExtDate  )), '''') END FROM MappingFile WHERE ColNum = @I)
SET @DDT = (SELECT CASE WHEN SUBSTRING(DataDate , 1, 3) = 'Var' THEN LTRIM(RTRIM(RIGHT(DataDate , LEN(DataDate  ) - 3))) WHEN DataDate      = '' THEN 'NULL' ELSE CONCAT('''', LTRIM(RTRIM(DataDate )), '''') END FROM MappingFile WHERE ColNum = @I)
SET @COL = (SELECT Variable  FROM MappingFile WHERE ColNum = @I)

EXEC('
    SELECT ''' + @DTS + ''',
            ' + @ID  + ',
            ' + @EDT + ',
            ' + @DDT + ',
            ''' + @COL + ''',
            ' + @COL + '        
    FROM ' + @DTS + '
    WHERE ' + @COL + ' IS NOT NULL
    ')