我有一个获取这些参数的存储过程:
@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'
有效!如果使用参数,为什么它不起作用?问候
答案 0 :(得分:5)
您在这里犯的错误是,您给人一种印象,那就是可以使用变量/参数代替对象的名称。简而言之,它不能。对象的名称必须是文字,因此您不能执行以下操作:
DECLARE @TableName sysname;
SET @TableName = N'MyTable';
SELECT *
FROM @TableName;
对于这种情况,您需要使用动态SQL,并且(同样重要)使用安全动态SQL。
首先,我将@ColumnName
更改为数据类型sysname
(这是nvarchar(128)
的同义词),并将@Checkbox
更改为bit
(复选框可以仅具有3个值:True
/ 1
,False
/ 0
和NULL
,因此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代码的内容。