2010-04-08 89 views
3

我试图使用sp_executesql的防止SQL注入SQL 2005中,我有这样一个简单的查询:sp_executesql的用“IN”声明

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 

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

+0

您是否解决了此问题?或者您是否可以向该问题添加更多详细信息? – 2010-04-09 11:28:42

回答

3

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

例如当@Code是X101时,B202然后查询正在运行为: SELECT * FROM表WHERE RegionCode IN('X101,B202') 因此,它正在查找包含@ P1的值的RegionCode。即使包含单引号,也就是说它在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

看起来问题是单个参数。实际上,你与结束了:

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' 

如果你要在该列表中两个以上的项目,虽然,你可能想要去另一条路线,可能与温度表或表值参数。

2

有很多方法可以在SQL Server中拆分字符串。本文介绍了几乎每一个方法的优点和缺点:

"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中的字符串,见前面的链接,这说明各的优点和缺点。

对于数字表的方法来工作,你需要做的这一次表的设置,这将创建一个包含从1到10000行的表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 

这里是工作示例尝试(必须有数字表和拆分功能设置在前):

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)