我正在使用MSSQL 2016,
我需要能够动态更新表上的行。
我有一个存储过程:
CREATE PROCEDURE sp_lookupData_UpdatelookupValues
(
@FullTableName nvarchar(50),
@Id nvarchar(10),
@Name nvarchar(50),
@Description nvarchar(50)
)
AS
BEGIN
DECLARE @Cmd nvarchar(150) = N'UPDATE ' + @FullTableName + ' SET Name = ' + @Name + ', Description = ' + @Description + ' WHERE ID = ' + @Id + '';
EXECUTE sp_executesql @Cmd;
END
问题是Name和Description值会像这样传递到@Cmd:
UPDATE TABLE_NAME SET Name = Private, Description = Default WHERE ID = 1
而不是私人'和'默认'。
结果是一个错误,其中Private被计为不存在的列(因为格式错误)。
Invalid column name 'Private'.
答案 0 :(得分:4)
在Private
和Default
周围使用单引号
而且由于您使用的是动态查询,因此您必须将单引号加倍才能逃脱它们。
DECLARE @Cmd nvarchar(150) = N'UPDATE ' + @FullTableName + ' SET Name = ''' + @Name + ''', Description = ''' + @Description + ''' WHERE ID = ' + @Id + '';
另外请确保您尝试下一个解决方案,因为第一个解决方案是SQL注入兼容的。
您也可以使用@Cmd
内的参数而不自行进行连接,但将参数传递给sp_executesql
另外,如果表格名称中有空格,我建议您QUOTENAME
@FullTableName
参数。
DECLARE @Cmd nvarchar(150) = N'UPDATE QUOTENAME(@FullTableName) SET Name = @Name, Description = @Description WHERE ID = @Id;'
EXEC sp_executesql @Cmd, @FullTableName, @Name, @Description, @Id;
这样做的好处是,您可以避免应用程序未检查的任何参数能够执行SQL注入。
答案 1 :(得分:0)
您必须自己添加引号。我更喜欢使用QUOTENAME来保持所有引号可识别:
QUOTENAME(@FullTableName, '''')
答案 2 :(得分:0)
您不能将参数用于标识符。但是,您可以使用值的参数:
DECLARE @Cmd nvarchar(150) = N'
UPDATE ' + @FullTableName + '
SET Name = @Name,
Description = @Description
WHERE ID = @Id';
EXECUTE sp_executesql @Cmd,
N'@Name nvarchar(50), @Description nvarchar(50), @Id nvarchar(10)',
@Name = @Name, @Description = @Description, @Id = @id;
不幸的是,动态表名称在SQL注入和语法错误方面仍然存在风险。我猜这是"控制"代码,而不是用户输入的代码,因此风险可能是可以接受的。但是,您可能应该使用quotename()
。
这引出了另一个问题,这可能是问题的症结所在。为什么有多个具有相同列的表?这些 - 应该这些 - 都可以存储在一个表中吗?这种类型的代码会质疑数据模型的各个方面。