2008-09-17 20 views
6

在C#中使用SqlCommand我在where子句中創建了一個包含IN(list ...)部分的查詢。而不是遍歷我的字符串列表生成我需要的查詢列表(如果你認爲在sqlInjection中是危險的)。我想,像我可以創建一個參數:通過C#中的參數在SqlCommand中的字符串列表

SELECT blahblahblah WHERE blahblahblah IN @LISTOFWORDS 

然後在代碼中,我嘗試添加一個參數是這樣的:

DataTable dt = new DataTable(); 
dt.Columns.Add("word", typeof(string)); 
foreach (String word in listOfWords) 
{ 
    dt.Rows.Add(word); 
} 
comm.Parameters.Add("LISTOFWORDS", System.Data.SqlDbType.Structured).Value = dt; 

但是,這是行不通的。

問題:

  • 我豈是不可能辦到的事?
  • 我採取了錯誤的方法?
  • 我在這種方法中有錯誤嗎?

感謝您的時間:)

回答

3

你正在嘗試做什麼是可能的,但不能使用你目前的方法。 SQL Server 2008之前的所有可能的解決方案都存在與性能,安全性和內存使用相關的折衷問題,這是一個非常普遍的問題。

This link shows some approaches for SQL Server 2000/2005

SQL Server 2008 supports passing a table value parameter.

我希望這有助於。

+4

第一個鏈接指向與第二個相同 – Matt 2009-06-12 17:59:12

+0

好的解決方案:) – graffic 2011-11-15 15:56:24

3

你要考慮其中該名單從何而來。通常這些信息在某個地方存在於數據庫中。例如,而不是這樣的:

SELECT * FROM [Table] WHERE ID IN (1,2,3) 

你可以使用一個子查詢是這樣的:

SELECT * FROM [Table] WHERE ID IN (SELECT TableID FROM [OtherTable] WHERE OtherTableID= @OtherTableID) 
+0

不幸的是,該信息不在另一張桌子上。是一些數據收集的結果。 但是,我會寫下來以備將來使用。謝謝 – graffic 2008-09-17 14:04:40

1
+0

第一個鏈接談到一個表變量。這是我用DataTAble創建的。但在這種情況下,我不能讓mssql吞下它。 我想這個想法是會插入那裏,然後delte通過guid。 – graffic 2008-09-17 14:12:21

0

如果你想通過列表作爲一個字符串參數,您可以動態構建查詢。

DECLARE @query VARCHAR(500) SET @query = 'SELECT等等等等WHERE blahblah在(' + @list + ')' EXECUTE(@query)

+1

相當SQL注入傾向:(但是,謝謝:) – graffic 2008-09-17 14:09:45

0

我曾經有同樣的問題,我認爲現在有辦法直接通過ADO.NET API執行此操作。

您可能會考慮將單詞插入到一個臨時表(加上一個queryid或其他東西),然後從查詢中引用該臨時表。或者動態創建查詢字符串,並避免通過其他度量(例如正則表達式檢查)進行sql注入。

+0

我會去第一個選項+批量sql插入。也許這可以節省我一些SQL I/O – graffic 2008-09-17 14:28:26

1
  • 我想嘗試一些不可能的事情嗎?

不,這不是不可能的。

  • 我採取了錯誤的做法?

你的做法是不工作(至少在.NET 2)

  • 我必須在這個方法的錯誤呢?

我會嘗試「Joel Coehoorn」解決方案(第二次答案),如果有可能的話。 否則,另一種選擇是發送一個「字符串」參數,其中包含由分隔符分隔的所有值。編寫一個動態查詢(基於字符串中的值構建它)並使用「exec」執行它。

另一個解決方案將是直接從代碼構建查詢。財產以後這樣的:

StringBuilder sb = new StringBuilder(); 
for (int i=0; i< listOfWords.Count; i++) 
{ 
    sb.AppendFormat("p{0},",i); 
    comm.Parameters.AddWithValue("p"+i.ToString(), listOfWords[i]); 
} 

comm.CommandText = string.Format(""SELECT blahblahblah WHERE blahblahblah IN ({0})", 
sb.ToString().TrimEnd(',')); 

的命令應該是:

SELECT blah WHERE blah IN (p0,p1,p2,p3...)...p0='aaa',p1='bbb' 

在MSSQL2005, 「IN」 與256個值僅工作。

0

這是一個老問題,但我想出了一個優雅的解決方案,我喜歡重複使用,我想其他人都會發現它很有用。

首先,您需要在SqlServer中創建一個FUNCTION,它接受一個分隔輸入並返回一個表項,並將其分成記錄。

以下是此下面的代碼:

ALTER FUNCTION [dbo].[Split] 
(
    @RowData nvarchar(max), 
    @SplitOn nvarchar(5) = ',' 
) 
RETURNS @RtnValue table 
(
    Id int identity(1,1), 
    Data nvarchar(100) 
) 
AS 
BEGIN 
    Declare @Cnt int 
    Set @Cnt = 1 

    While (Charindex(@SplitOn,@RowData)>0) 
    Begin 
     Insert Into @RtnValue (data) 
     Select 
      Data = ltrim(rtrim(Substring(@RowData,1,Charindex(@SplitOn,@RowData)-1))) 

     Set @RowData = Substring(@RowData,Charindex(@SplitOn,@RowData)+1,len(@RowData)) 
     Set @Cnt = @Cnt + 1 
    End 

    Insert Into @RtnValue (data) 
    Select Data = ltrim(rtrim(@RowData)) 

    Return 
END 

你現在可以做這樣的事情:

Select Id, Data from dbo.Split('123,234,345,456',',') 

並不懼怕,這不可能是容易受到SQL注入攻擊。

接下來寫一個存儲過程,需要以逗號分隔的數據,然後你可以寫一個使用這種分割功能的SQL語句:

CREATE PROCEDURE [dbo].[findDuplicates] 
    @ids nvarchar(max) 
as 
begin 
    select ID 
     from SomeTable with (nolock) 
    where ID in (select Data from dbo.Split(@ids,',')) 
end 

現在,你可以寫一個C#包裝周圍:

public void SomeFunction(List<int> ids) 
{ 
    var idsAsDelimitedString = string.Join(",", ids.Select(id => id.ToString()).ToArray()); 

    // ... or however you make your connection 
    var con = GetConnection(); 

    try 
    { 
     con.Open(); 

     var cmd = new SqlCommand("findDuplicates", con); 

     cmd.Parameters.Add(new SqlParameter("@ids", idsAsDelimitedString)); 

     var reader = cmd.ExecuteReader(); 

     // .... do something here. 

    } 
    catch (Exception) 
    { 
     // catch an exception? 
    } 
    finally 
    { 
     con.Close(); 
    } 
} 
相關問題