2010-04-08 47 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)