使用参数更新表

时间:2019-01-03 18:47:54

标签: sql sql-server tsql

我有一个获取这些参数的存储过程:

@TaskId        UNIQUEIDENTIFIER,
@ColumnName    VARCHAR(255) = NULL,
@CheckBoxValue VARCHAR(255) = NULL

然后我有一个UPDATE语句,如下所示:

UPDATE [RedMarkItems]
SET @ColumnName = @CheckBoxValue
WHERE [TaskId] = @TaskId

如果我运行存储过程,如:

exec usp_RedMarkItem_Insert 
         @TaskId = '82ab0c4b-9342-46fa-acbe-c00b87571bf9', 
         @ColumnName = Item7, 
         @CheckBoxValue = 1, 
         @CurrentUser = '6074caea-7a8e-4699-9451-16c2eaf394ef'

它不会影响表格,只是说

  

命令成功完成

但是值仍然相同,但是如果我在UPDATE语句中替换值

UPDATE [RedMarkItems]
SET Item7 = 1
WHERE [TaskId] = '82ab0c4b-9342-46fa-acbe-c00b87571bf9'

有效!如果使用参数,为什么它不起作用?问候

2 个答案:

答案 0 :(得分:5)

您在这里犯的错误是,您给人一种印象,那就是可以使用变量/参数代替对象的名称。简而言之,它不能。对象的名称必须是文字,因此您不能执行以下操作:

DECLARE @TableName sysname;
SET @TableName = N'MyTable';

SELECT *
FROM @TableName;

对于这种情况,您需要使用动态SQL,并且(同样重要)使用安全动态SQL。

首先,我将@ColumnName更改为数据类型sysname(这是nvarchar(128)的同义词),并将@Checkbox更改为bit(复选框可以仅具有3个值:True / 1False / 0NULL,因此nvarchar(255)是非常差的数据选择)。然后您的存储过程将类似于:

DECLARE @SQL nvarchar(MAX);

SELECT @SQL = N'UPDATE RedMarkItems' + NCHAR(10) +
              N'SET ' + QUOTENAME(c.[name]) + N' = @Checkbox' + NCHAR(10) +
              N'WHERE TaskID = @ID;'
FROM sys.tables t
     JOIN sys.columns c ON t.object_id = c.object_id
WHERE c.[name] = @ColumnName
  AND t.[name] = 'RedMarkItems';

EXEC sp_executesql @SQL, N'@Checkbox bit, @ID uniqueindentifier', @Checkbox = @CheckBoxValue, @ID = @TaskId;

引用sys表的原因是为了确保该列确实存在。如果没有,则不会运行任何SQL。除了使用QUOTENAME之外,这只是一种额外的安全措施。

答案 1 :(得分:2)

您所做的设置是@ColumnName等于@CheckBoxValue中的值0次或更多次(基于表中存在的行数)。可能不是您想要的...

相反,您将要使用动态SQL(set @sql = 'UPDATE … ' + QUOTENAME(@ColumnName) + 'rest of sql')或以其他方式基于要动态更新的值构建case语句来处理每一列。 SQL需要在编译时绑定该语句,因此您需要在编译时为查询处理器提供可用的语句,以确保该列是真实的,具有正确的类型以进行类型派生,等等。阻止所有这些逻辑都起作用(假设语义符合您在所发布问题中的预期)。

请小心,因为可能会对未验证的参数进行SQL注入攻击。您需要确保该列名是有效的列名,而不是允许在正在运行的事务的上下文中执行任意SQL代码的内容。