Работая с SqlCommand в C#, я создал запрос, который содержит часть IN (список ...) в предложении where. Вместо того, чтобы перебирать мой список строк, генерируя список, который мне нужен для запроса (опасно, если вы думаете в sqlInjection). Я думал, что могу создать такой параметр, как:
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;
Но это не работает.
Вопросов:
Спасибо за ваше время :)





Вы хотите подумать, откуда взялся этот список. Обычно эта информация находится где-то в базе данных. Например, вместо этого:
SELECT * FROM [Table] WHERE ID IN (1,2,3)
Вы можете использовать такой подзапрос:
SELECT * FROM [Table] WHERE ID IN ( SELECT TableID FROM [OtherTable] WHERE OtherTableID= @OtherTableID )
Я бы рекомендовал установить параметр как строку значений, разделенных запятыми, и использовать функцию разделения в SQL, чтобы превратить ее в таблицу значений с одним столбцом, а затем вы можете использовать функцию IN.
http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=50648 - функции разделения
Это довольно склонно к внедрению Sql :(
Если я правильно понимаю, вы пытаетесь передать список в качестве параметра SQL.
Некоторые люди пытались сделать это раньше с ограниченным успехом:
Передача массивов в хранимые процедуры
Передача массива значений в SQL Server без обработки строк
Использование возможностей XML в MS SQL 2005 для передачи списка значений команде
Первая ссылка говорит о табличной переменной. Это то, что я создал с помощью DataTAble. Но в этом случае я не могу заставить mssql проглотить его. Думаю, идея состоит в том, чтобы создать руководство по таблице, слово. Вставьте тут же дельте по guid.
Если вы хотите передать список в виде строки в параметре, вы можете просто создать запрос динамически.
ОБЪЯВИТЬ @query varchar (500) SET @query = 'ВЫБРАТЬ бла-бла, ГДЕ бла-бла в (' + @list + ')' ВЫПОЛНИТЬ (@query)
Раньше у меня была такая же проблема, думаю, теперь есть способ сделать это напрямую через ADO.NET API.
Вы можете подумать о том, чтобы вставить слова в искушаемый объект (плюс queryid или что-то в этом роде), а затем сослаться на это искушение из запроса. Или динамически создавать строку запроса и избегать внедрения sql другими мерами (например, проверками регулярных выражений).
Я выберу первый вариант + объемную вставку sql. Возможно, это поможет мне сэкономить на вводе-выводе SQL
То, что вы пытаетесь сделать, возможно, но не используя ваш текущий подход. Это очень распространенная проблема со всеми возможными решениями до SQL Server 2008, имеющими компромиссы, связанные с производительностью, безопасностью и использованием памяти.
Эта ссылка показывает некоторые подходы для SQL Server 2000/2005.
SQL Server 2008 поддерживает передачу параметра значения таблицы.
Надеюсь, это поможет.
Нет, это не невозможно.
Ваш подход не работает (по крайней мере, в .net 2)
Я бы попробовал решение "Джоэла Кохорна" (2-й ответ), если это возможно. В противном случае другой вариант - отправить «строковый» параметр со всеми значениями, разделенными разделителем. Напишите динамический запрос (построите его на основе значений из строки) и выполните его с помощью «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 значениями.
В нижнем запросе я получаю сообщение об ошибке при первом появлении p0.
Это старый вопрос, но я придумал для него элегантное решение, которое я люблю повторно использовать, и я думаю, что все остальные сочтут его полезным.
Прежде всего, вам нужно создать 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();
}
}
К сожалению, информации нет в другой таблице. Это результат сбора некоторых данных. Но это хороший подход, который я запишу для использования в будущем. Спасибо