2008-09-17 2 views
6

Работа с SqlCommand в C# Я создал запрос, содержащий часть IN (list ...) в предложении where. Вместо того, чтобы перебирать мой строковый список, генерирующий список, который мне нужен для запроса (опасно, если вы думаете в sqlInjection). Я думал, что я мог бы создать параметр, как:Строковый список в SqlCommand через параметры в C#

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

0

Я бы рекомендовал установить параметр в виде разделенных запятыми строки значений и использовать Split функцию в SQL для превратите это в одну таблицу столбцов значений, а затем вы можете использовать функцию IN.

http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=50648 - Сплит Функции

+0

Это довольно Sql Injection prone :( – graffic 2008-09-17 14:05:59

1

Если я понимаю правильно, вы пытаетесь передать список в качестве параметра SQL.

Некоторые люди пытались это раньше с ограниченным успехом:

Passing Arrays to Stored Procedures

Arrays and Lists in SQL 2005

Passing Array of Values to SQL Server without String Manipulation

Using MS SQL 2005's XML capabilities to pass a list of values to a command

+0

Первая ссылка рассказывает о переменной таблицы. Это то, что я создал с помощью DataTAble. Но в этом случае я не могу заставить mssql проглотить его. Я предполагаю, что идея gonna be create table guid, word. Вставьте туда-сюда delte by guid. – graffic 2008-09-17 14:12:21

0

Если вы хотите передать список в виде строки в параметр, вы можете просто построить запрос динамически ,

DECLARE @query VARCHAR (500) SET @query = 'SELECT бла-бла WHERE blahblah в (' + @list + ')' Execute (@query)

+1

Quince SQL Injection prone :(Но спасибо :) – 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» (2 ответы), если возможно. В противном случае другой параметр должен отправить параметр «строка» со всеми значениями, разделенными разделителем. Напишите динамический запрос (постройте его на основе значений из строки) и выполните его с помощью «exec».

Другим решением будет о построить запрос непосредственно из кода. Somthing так:

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

Это старый вопрос, но я придумал элегантное решение для этого, что я люблю использовать повторно, и я думаю, что все еще будет полезен.

Прежде всего вам необходимо создать FUNCTION в SqlServer, который берет ввод с разделителями и возвращает таблицу с элементами, разделенными на записи.

Вот следующий код для этого:

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(); 
    } 
} 
Смежные вопросы