Как выполнить SQL-запрос со списком значений String с использованием формата «WHERE [columnname] IN [values]» в ASP.NET? - PullRequest
1 голос
/ 11 марта 2011

У меня есть запрос SQL, который я выполняю на странице ASP.NET. Окончательный анализируемый SQL должен содержать список строковых значений в формате WHERE [columnname] IN [values]. Например, окончательный запрос может выглядеть примерно так:

SELECT PRODUCTNAME FROM PRODUCT WHERE PRODUCTCODE IN ('ABC','DEF','GHI','JKL', /* etc */);

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

String[] productCodes = { "ABC", "DEF", "GHI", "JKL" };
SqlCommand cmd = "SELECT PRODUCTNAME FROM PRODUCT WHERE PRODUCTCODE IN (@ProductCodes)";
cmd.Parameters.Add("@ProductCodes", productCodes);

Однако такого рода функциональности в .NET не существует. Как я должен идти о реализации этого? Я мог бы использовать цикл foreach для массива и запускать запрос с одним значением в качестве параметра для каждого значения, но потенциально в массиве могло быть около сотни различных значений, и кажется, что запрос их по отдельности будет очень неэффективным.

Я прочитал другой вопрос, где кто-то предлагал решение для строго типизированных int-параметров , но этот метод заставил бы меня нервничать по поводу SQL-инъекции при использовании со значениями String, тем более что клиент может очень хорошо уметь влиять на входные значения.

Как бы вы реализовали эту функцию запроса?

РЕДАКТИРОВАТЬ с подробностями БД :

База данных - SQL Server 2005. Извините, что забыл упомянуть об этом.

Ответы [ 4 ]

2 голосов
/ 12 марта 2011

Создайте свой базовый SQL-оператор в виде формата, добавьте параметры динамически и затем установите значения в цикле.

String[] productCodes = { "ABC", "DEF", "GHI", "JKL" };
string sqlFormat = "SELECT PRODUCTNAME FROM PRODUCT WHERE PRODUCTCODE IN ({0})";
var @params = productCodes.Select((id, index) => String.Format("@id{0}", index)).ToArray();
var sql = String.Format(sqlFormat, string.Join(",", @params));

using(var cmd = new DbCommand(sql))
{
    for (int i = 0; i < productCodes.Length; i++)
        cmd.Parameters.Add(new Parameter(@params[i], DbType.String, productCodes[i]));
    // execute query
}
1 голос
/ 12 марта 2011

Просто идея:

String[] productCodes = { "ABC", "DEF", "GHI", "JKL" };
SqlCommand cmd = "SELECT PRODUCTNAME FROM PRODUCT WHERE PRODUCTCODE IN ("; 

for (int i=0;i<productCodes.Length;i++) {
  cmd.CommandText += "@" + productCodes[i] + ",";
  cmd.Parameters.Add("@" + productCodes[i], productCodes[i]);
}
cmd.CommandText = cmd.CommandText.SubString(0, cmd.CommandText.Length-1);
cmd.CommandText += ");"

Возможно, не самый лучший способ, но я думаю, что именно так я и попробую.

0 голосов
/ 12 марта 2011

Для записи запрос сотен или около того значений в массиве «не по отдельности» также может быть неэффективным (хотя и не таким неэффективным, как сто или около того обращений к SQL Server).Однако параметризация запроса немного помогает, потому что вы можете подготовить один и тот же запрос и выполнить его несколько раз.Хранимая процедура может быть даже лучше.

Любой шанс, что вы можете использовать Linq, или вы до .NET-3.5?

Если вы не можете использовать Linq, и вы обязательно должны пойтиэтот маршрут, попробуйте следующее:

SqlCommand cmd = "SELECT PRODUCTNAME FROM PRODUCT WHERE PRODUCTCODE = @ProductCode";
cmd.Prepare();
List<string> results;
foreach (string code in productCodes)
{
    cmd.Parameters.Clear();
    cmd.Parameters.Add("@ProductCodes", DbType.VarChar).Value = code;
    cmd.ExecuteQuery();
    // Add code here to add the returned values to the results list.  It's been
    // a while since I've used ADO.NET, and I don't have time to look it up
    // at the moment...
}

И затем у вас есть список результатов.

0 голосов
/ 12 марта 2011

Вы ищете табличных параметров. Однако, они не были доступны для сервера sql до тех пор, пока не был широко принят asp.net, и поэтому поддержка их в asp.netограничено.

Вместо этого я рекомендую думать об этом как о создании корзины для покупок.Пользователи добавляют товары в корзину, и в какой-то момент вы хотите отобразить все товары в корзине.В этом случае естественным решением является то, что сама корзина покупок находится скорее в таблице базы данных.Вместо того, чтобы извлекать элементы для карты, которые будут включены как часть директивы «IN (?)», Вы вместо этого создаете ее как подзапрос для вашей директивы: «WHERE X IN (SELECT X FROM ShoppingCart WHERE UserID= @UserID AND SessionKey= @SessionKey)».Даже если вы добавили сотни элементов, пользователь будет очень быстр, и нагрузка на каждую вставку распределяется довольно равномерно.

Конечно, вы, вероятно, создаете что-то иное, чем корзина для покупок., но тем не менее ваш запрос почти всегда будет попадать в одну из трех категорий:

  1. Элементы для вашего списка выбираются пользователем вручную, и в этом случае вы все равно можете получить каждый результат выбора в новомзапись базы данных, которая, в свою очередь, может быть использована в подзапросе
  2. Это данные, которые уже доступны в вашей базе данных, и в этом случае вы все равно сможете использовать подзапрос (а если нет, это может бытьвремя добавить куда-нибудь столбец «категория», чтобы вы могли это сделать).
  3. Это данные, которые жестко запрограммированы в вашем приложении, и в этом случае вы также можете кодировать их в свой запрос

Редкое исключение - когда запрос инициируется другим источником компьютера, или у вас также может быть много кода, который вы неохотно перерабатываете, чтобы сделать эту возможностьe.
Так что, если по какой-либо причине этот подход вам не подходит, стандартную статью по теме для сервера sql (включая несколько альтернатив) можно найти здесь:
http://www.sommarskog.se/arrays-in-sql.html

Эта статья действительно является стандартной работой над предметом и стоит вашего времени.

...