带有'IN'语句的sp_executesql

时间:2010-04-08 13:53:11

标签: sql sql-server-2005 tsql sp-executesql

我正在尝试使用sp_executesql来防止SQL 2005中的SQL注入,我有一个像这样的简单查询:

SELECT * from table WHERE RegionCode in ('X101', 'B202')

但是,当我使用sp_executesql执行以下操作时,它不会返回任何内容。

Set @Cmd = N'SELECT * FROM table WHERE RegionCode in (@P1)'
SET @ParamDefinition = N'@P1 varchar(100)';
DECLARE @Code as nvarchar(100);
SET @Code = 'X101,B202'
EXECUTE sp_executesql @Cmd, @ParamDefinition, @P1 = @Code

这是我测试过的:

SET @Code = 'X101'   <-- This works, it returns a single region
SET @Code = 'X101,B202'   <--- Returns nothing
SET @Code = '''X101'',''B202'''  <-- Returns nothing

请帮助....我做错了什么?

3 个答案:

答案 0 :(得分:3)

它不起作用的原因是因为@ P1被视为一个单值。

e.g。当@Code是X101,B202然后查询正在运行为: SELECT * FROM表WHERE RegionCode IN('X101,B202') 因此,它正在寻找一个RegionCode,其值包含在@ P1中。即使你包含单引号,所有这意味着它在RegionCode中搜索的值应该包含那些单引号。

您需要将@Code变量实际连接到@Cmd sql命令文本中,以使其按照您的思路运行:

SET @Code = '''X101'',''B202'''
SET @Cmd = 'SELECT * FROM Table WHERE RegionCode IN (' + @Code + ')'
EXECUTE (@Cmd)

显然,这只会让你注意到SQL注入,所以如果你采用这种方法来确保你防范它,你需要非常小心。

还有其他方法可以处理这种情况,您希望传入要搜索的动态值列表。

查看my blog上有关可以与SQL Server 2005一起使用的两种方法的示例。一种方法是以“Value1,Value2,Value3”的形式传入CSV列表,然后将其拆分为TABLE变量使用用户定义的功能(如果你快速谷歌或搜索这个网站,有很多提到这种方法)。分离后,然后将TABLE var加入到主查询中。第二种方法是传入包含值的XML blob并使用SQL Server的内置XML功能。这两种方法都通过该链接中的性能指标进行了演示,并且它们不需要动态SQL。

如果您使用的是SQL Server 2008,那么表值参数将是最佳选择 - 这是我在该链接中展示的第三种方法,该方法最佳。

答案 1 :(得分:2)

在SQL Server中分割字符串的方法有很多种。本文涵盖几乎所有方法的PRO和CON:

"Arrays and Lists in SQL Server 2005 and Beyond, When Table Value Parameters Do Not Cut it" by Erland Sommarskog

您需要创建拆分功能。这就是如何使用拆分功能:

SELECT
    *
    FROM YourTable                               y
    INNER JOIN dbo.yourSplitFunction(@Parameter) s ON y.ID=s.Value

I prefer the number table approach to split a string in TSQL但是有很多方法可以在SQL Server中拆分字符串,请参阅上一个链接,该链接解释了每个链接的PRO和CON。

要使Numbers Table方法起作用,您需要进行一次性表设置,这将创建一个包含1到10,000行的表Numbers

SELECT TOP 10000 IDENTITY(int,1,1) AS Number
    INTO Numbers
    FROM sys.objects s1
    CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)

设置Numbers表后,创建此拆分功能:

CREATE FUNCTION [dbo].[FN_ListToTable]
(
     @SplitOn  char(1)      --REQUIRED, the character to split the @List string on
    ,@List     varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN 
(

    ----------------
    --SINGLE QUERY-- --this will not return empty rows
    ----------------
    SELECT
        ListValue
        FROM (SELECT
                  LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(@SplitOn, List2, number+1)-number - 1))) AS ListValue
                  FROM (
                           SELECT @SplitOn + @List + @SplitOn AS List2
                       ) AS dt
                      INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
                  WHERE SUBSTRING(List2, number, 1) = @SplitOn
             ) dt2
        WHERE ListValue IS NOT NULL AND ListValue!=''

);
GO 

现在,您可以轻松地将CSV字符串拆分为表格并加入其中,或者根据需要使用它,即使是在动态sql中也是如此。以下是如何在您的问题的动态参数化查询中使用它:

DECLARE @Cmd as nvarchar(1000),@ParamDefinition nvarchar(1000);
Set @Cmd = N'SELECT * FROM table WHERE RegionCode in (SELECT ListValue FROM dbo.FN_ListToTable('','',@P1))'
SET @ParamDefinition = N'@P1 varchar(100)';
DECLARE @Code as nvarchar(1000);
SET @Code = 'X101,B202'
EXECUTE sp_executesql @Cmd, @ParamDefinition, @P1 = @Code

HERE是一个可以试用的工作样本(必须首先设置数字表和拆分功能):

CREATE TABLE YourTable (PK int primary key, RowValue varchar(5))
INSERT YourTable VALUES (1,'A')
INSERT YourTable VALUES (2,'BB')
INSERT YourTable VALUES (3,'CCC')
INSERT YourTable VALUES (4,'DDDD')
INSERT YourTable VALUES (5,'EEE')
INSERT YourTable VALUES (6,'FF')
INSERT YourTable VALUES (7,'G')

DECLARE @SQL              nvarchar(1000)
       ,@ParamDefinition  nvarchar(1000)
       ,@ParamValue       varchar(100)
SELECT @SQL = N'SELECT * FROM YourTable WHERE PK IN (SELECT ListValue FROM dbo.FN_ListToTable('','',@P1))'
      ,@ParamDefinition = N'@P1 varchar(100)'
      ,@ParamValue = '2,4,,,6,,8,,2,,4'
EXECUTE sp_executesql @SQL, @ParamDefinition, @P1 = @ParamValue

输出:

PK          RowValue
----------- --------
2           BB
4           DDDD
6           FF

(3 row(s) affected)

答案 2 :(得分:1)

看起来问题是单个参数。实际上,你最终得到:

SELECT * from table WHERE RegionCode in ('X101,B202')

SELECT * from table WHERE RegionCode in ('''X101'', ''B202''')

也就是说,RegionCode必须等于'X101,B202'''X101','B202''(完整字符串)才能工作。

你最好的选择是在这里使用两个参数:

Set @Cmd = N'SELECT * FROM table WHERE RegionCode in (@P1,@P2)'
SET @Code1 = 'X101'
SET @Code2 = 'B202'

如果您要在该列表中包含两个以上的项目,您可能想要使用其他路径,可能使用临时表或表值参数。

相关问题