Список строк в SqlCommand через параметры в C#

Работая с 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;

Но это не работает.

Вопросов:

  • Я пытаюсь сделать что-то невозможное?
  • Я сделал неправильный подход?
  • Есть ли у меня ошибки в таком подходе?

Спасибо за ваше время :)

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
6
0
27 870
8
Перейти к ответу Данный вопрос помечен как решенный

Ответы 8

Вы хотите подумать, откуда взялся этот список. Обычно эта информация находится где-то в базе данных. Например, вместо этого:

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

Вы можете использовать такой подзапрос:

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

К сожалению, информации нет в другой таблице. Это результат сбора некоторых данных. Но это хороший подход, который я запишу для использования в будущем. Спасибо

graffic 17.09.2008 18:04

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

http://www.sqlteam.com/forums/topic.asp?TOPIC_ID=50648 - функции разделения

Это довольно склонно к внедрению Sql :(

graffic 17.09.2008 18:05

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

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

Передача массивов в хранимые процедуры

Массивы и списки в SQL 2005

Передача массива значений в SQL Server без обработки строк

Использование возможностей XML в MS SQL 2005 для передачи списка значений команде

Первая ссылка говорит о табличной переменной. Это то, что я создал с помощью DataTAble. Но в этом случае я не могу заставить mssql проглотить его. Думаю, идея состоит в том, чтобы создать руководство по таблице, слово. Вставьте тут же дельте по guid.

graffic 17.09.2008 18:12

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

ОБЪЯВИТЬ @query varchar (500) SET @query = 'ВЫБРАТЬ бла-бла, ГДЕ бла-бла в (' + @list + ')' ВЫПОЛНИТЬ (@query)

Раньше у меня была такая же проблема, думаю, теперь есть способ сделать это напрямую через ADO.NET API.

Вы можете подумать о том, чтобы вставить слова в искушаемый объект (плюс queryid или что-то в этом роде), а затем сослаться на это искушение из запроса. Или динамически создавать строку запроса и избегать внедрения sql другими мерами (например, проверками регулярных выражений).

Я выберу первый вариант + объемную вставку sql. Возможно, это поможет мне сэкономить на вводе-выводе SQL

graffic 17.09.2008 18:28
Ответ принят как подходящий

То, что вы пытаетесь сделать, возможно, но не используя ваш текущий подход. Это очень распространенная проблема со всеми возможными решениями до 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.

micahhoover 03.02.2015 16:49

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

Прежде всего, вам нужно создать 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();
    }
}

Другие вопросы по теме