SQL:在“=”附近使用带变量的 CASE 语句时语法不正确

SQL : Getting Incorrect Syntax near '=' While using CASE statement with variables

我正在尝试执行以下查询,其中使用了一些变量。下面的 SQL 代码是存储过程的一部分。想法是根据 FileKey.

动态设置目标列及其值
DECLARE @TargetColNames nvarchar(max) = '';
DECLARE @SourceColNames nvarchar(max) = '';
DECLARE @SourceColNamesInsert nvarchar(max) = '';
DECLARE @UpdateColumns nvarchar(max) = '';

SELECT 
    CASE 
        WHEN @FileKey IN ('s_1','s_2') 
            THEN @TargetColNames = @TargetColNames + ' [CreatedUser], [UpdatedUser], [CreatedDateTime],[UpdatedDateTime],[IsDeleted],[DeletedOn]'     
            ELSE @TargetColNames = @TargetColNames + ' [CreatedUser], [UpdatedUser], [CreatedDateTime], [UpdatedDateTime]' 
    END,
    @SourceColNames = CONCAT('CreatedUser','UpdatedUser','CreatedDateTime', 'UpdatedDateTime'),
    @SourceColNamesInsert = CONCAT(''',@User, ''',''',@User, ''', 'Getdate()', 'Getdate()' ),
    CASE 
        WHEN @FileKey IN ('s_1','s_2') 
            THEN @UpdateColumns = CONCAT('Target.UpdatedUser= ''',@User,''', 'Target.[IsDeleted]=0','Target.[DeletedOn]=null')
            ELSE @UpdateColumns = CONCAT('Target.UpdatedUser= ''',@User,''', 'Target.UpdatedDateTime=Getdate()')
    END

上面的 SQL 语句抛出错误:

Msg 102, Level 15, State 1, Procedure uspDynamicStageToPropLayer1, Line 165 [Batch Start Line 5]
Incorrect syntax near '='.

我在这里错过了什么?也许这是一个很愚蠢的错误...

此外,通过对引用的部分执行 concat,您可能允许 SQL injection 进入您的查询,甚至是动态构建。如果用户名(或强制参数前导单引号,那么垃圾注入如

@User = ';drop table X --

话虽如此,有些东西可以更简化,例如

SELECT
   @TargetColNames = @TargetColNames
       + ' [CreatedUser], [UpdatedUser], [CreatedDateTime], [UpdatedDateTime]'
       + CASE WHEN @FileKey IN ('s_1','s_2')
              THEN ', [IsDeleted], [DeletedOn]'
              else ''
         end

对于插入,您可能会失败。如果您查看 @User

的可能结果
@SourceColNamesInsert = concat(''',@User, ''',''',@User, ''',
'Getdate()', 'Getdate()' ),

将得到低于该值的结果 而不是 我认为的预期值。请注意值之间没有逗号,因为三重 ' 正在创建开始和结束文字,并且列插入值之间没有实际逗号。

',@User, '',@User, 'Getdate()Getdate()

但是……

select concat('''@User'', ''@User''', ', Getdate(), Getdate()' );

这将导致...

'@User', '@User', Getdate(), Getdate()

''' 实际上会立即创建一个带引号的开头字符串和它后面的值,然后 '' (double) 关闭引号字符串,但它还会在下一个 '' (double) 之前添加逗号分隔符开始第二个用户和'''(三重)关闭第二个@User,然后添加逗号和两个getdate()调用。

'@User', '@User', Getdate(), Getdate()

现在,如果@User 的值为 'Bob',而您的意图是将字符串输出为

'Bob', 'Bob', Getdate(), Getdate()

改为 select concat('''', @User, ''', ','''', @User, '''', ', Getdate(), Getdate()' );

''''(四边形)表示我想打开一个字符串,使用单引号(由内部两个 ''),然后将其作为自己的字符串关闭。 然后 获取@User 的。然后是一个开放字符串 ' 和 '' 用于关闭名称周围的引号,然后打开一个单引号开始下一个,在开始第二个用户的下一个引号之前的逗号并通过 '''' 关闭它,然后再次输入用户的 value,最后用右引号 '''' 关闭第二个用户。最后添加逗号和 getdate() 调用。是的,引用中的愚蠢棘手。

没有 CONCAT() 的更简单的实现是在每个显式部分之间使用 +,例如

select '''' + @User + '''' + ', ' + '''' + @User + '''' + ', Getdate(), getdate()' ;

其中每个 '''' 都是单引号,因此导致

' + Bob + ' + , + ' + Bob + ' + , Getdate(), getdate()

导致

'Bob', 'Bob', Getdate(), getdate()

我将把最终的 UpdateColumns 留给您来确认您的预期输出。

但是,如前所述,当您动态构建具有嵌入参数值的 SQL 语句时,注意可能的 SQL 注入 似乎正在做。

这是解决此问题的另一种方法:

Declare @FileKey                varchar(10) = 's_3'
      , @TargetColNames         nvarchar(max) = ''
      , @SourceColNames         nvarchar(max) = ''
      , @SourceColNamesInsert   nvarchar(max) = ''
      , @UpdateColumns          nvarchar(max) = '';

    Set @TargetColNames = concat_ws(', ', '[CreatedUser]', '[UpdatedUser]', '[CreatedDateTime]', '[UpdatedDateTime]');

     If @FileKey In ('s_1', 's_2')
        Set @TargetColNames = concat_ws(', ', @TargetColNames, '[UpdatedDateTime]', '[IsDeleted]', '[DeletedOn]');

  Print @TargetColNames;

    Set @SourceColNames = concat_ws(', ', '[CreatedUser]', '[UpdatedUser]', '[CreatedDateTime]', '[UpdatedDateTime]');
    Set @SourceColNamesInsert = concat_ws(', ', quotename('@User', char(39)), quotename('Getdate()', char(39)), quotename('Getdate()', char(39)));

  Print @SourceColNames;
  Print @SourceColNamesInsert;