将变量连接成SQL字符串的方法

16

我需要将一个变量表名拼接到我的SQL查询中,例如以下内容...

ALTER FUNCTION fn_myfunction(@KeyValue text)
BEGIN
     INSERT INTO @tmpTbl1
     SELECT @KeyValue AS fld1
     FROM tbl + @KeyValue + KeyValue.fld1

我也尝试了以下操作,但它告诉我必须声明表变量?

ALTER FUNCTION fn_myfunction(@KeyValue text, @KeyTable text)
     FROM @KeyTable.fld1
2个回答

24

如果我理解你想要做什么的话,你可以使用动态SQL来实现。关键是你需要创建一个包含SQL语句的字符串。这是因为在执行语句时,表名必须在实际的SQL文本中指定。表参考和列引用不能作为参数提供,它们必须出现在SQL文本中。

因此,你可以采用以下方法:

SET @stmt = 'INSERT INTO @tmpTbl1 SELECT ' + @KeyValue 
    + ' AS fld1 FROM tbl' + @KeyValue

EXEC (@stmt)

首先,我们创建一个SQL语句作为一个字符串。假设给定一个@KeyValue为'Foo',那么会创建一个包含以下内容的字符串:

'INSERT INTO @tmpTbl1 SELECT Foo AS fld1 FROM tblFoo'

此时,它只是一个字符串。但我们可以使用EXECUTE(或简写为EXEC)作为动态SQL语句来执行字符串的内容。

老派的sp_executesql过程是EXEC的替代品,另一种执行动态SQL的方式,它也允许您传递参数,而不是在语句文本中指定所有值为文字。


后续

EBarr指出(正确且重要),此方法容易受到SQL注入攻击。

考虑如果@KeyValue包含以下字符串会发生什么:

'1 AS foo; DROP TABLE students; -- '

我们将产生的字符串作为SQL语句如下:

'INSERT INTO @tmpTbl1 SELECT 1 AS foo; DROP TABLE students; -- AS fld1 ...'

当我们将该字符串作为SQL语句执行时:

INSERT INTO @tmpTbl1 SELECT 1 AS foo;
DROP TABLE students;
-- AS fld1 FROM tbl1 AS foo; DROP ...

不只是DROP TABLE指令容易受到注入攻击,任何SQL指令都有可能遭到注入攻击,而且这种攻击可能更加微妙甚至更加恶意。第一轮攻击可能是为了获取关于表和列的信息,接着就会尝试检索数据(例如电子邮件地址、账号等)。

解决这个漏洞的一种方法是验证@KeyValue的内容,比如说它只应该包含字母和数字字符(例如使用LIKE '%[^A-Za-z0-9]%' 检查是否存在不在这些范围内的字符)。如果发现非法字符,则拒绝该值,并退出执行任何SQL指令。


1
这很可能是根据要求的最佳解决方案。然而,需要注意的是,这是 SQL 注入攻击的易受攻击路径。 - EBarr
我从我的回答中省略了 "+ KeyValue.fld1",因为不清楚它是否应该是表名的一部分。(它将被连接起来,但是表名会包含一个句点,虽然这是有效的(如果表名用方括号括起来),但这将是非常不规则的。 - spencer7593

7
你可以像这样利用Prepared Statements
set @query = concat( "select name from " );
set @query = concat( "table_name"," [where condition] " );

prepare stmt from @like_q;
execute stmt;

2
“@like_q” 是指这里的 “@query” 吗?你是想要覆盖 “@query”的值吗? - JTech
Msg 195,级别 15,状态 10,行 3 “concat”不是已知的内置函数名称。 - Unbound

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接