Добавить список <int>к параметру mysql - PullRequest
18 голосов
/ 15 апреля 2011

У меня есть этот вопрос о MySqlParameter из коннектора .NET.

У меня есть этот запрос:

SELECT * FROM table WHERE id IN (@parameter)

И MySqlParameter:

intArray = new List<int>(){1,2,3,4};

...connection.Command.Parameters.AddWithValue("parameter", intArray);

Это возможно?Можно ли передать массив int в один MySqlParameter?Другое решение будет преобразовывать массив int в строку, такую ​​как «1,2,3,4», но когда я передаю его в MySqlParameter и это распознается как строка, он помещает в SQL-запрос как«1 \, 2 \, 3 \, 4», и это не возвращает ожидаемых значений.

@ ОБНОВЛЕНИЕ: Похоже, что команда соединителя mysql должна работать немного тяжелее.

Ответы [ 8 ]

15 голосов
/ 20 апреля 2011

когда я передаю его в MySqlParameter, и он распознается как строка, он помещает в SQL-запрос, например, "1 \, 2 \, 3 \, 4", и это не возвращает ожидаемых значений.

Я столкнулся с этим прошлой ночью. Я обнаружил, что FIND_IN_SET работает здесь:

SELECT * FROM table WHERE FIND_IN_SET(id, @parameter) != 0
...
intArray = new List<int>(){1,2,3,4};
conn.Command.Parameters.AddWithValue("parameter", string.Join(",", intArray));

Очевидно, что это имеет некоторые ограничения по длине (я нашел ваше сообщение в поисках альтернативного решения), но это может сработать для вас.

5 голосов
/ 15 апреля 2011

Параметры не работают с IN. Я всегда встраивал такие вещи как строку в сам запрос. Хотя это обычно считается дурным тоном, поскольку внедрение SQL-кода, если вы строите запрос из строго типизированного числового списка, не должно быть никакой возможности какого-либо внешнего ввода повредить его осмысленным образом.

3 голосов
/ 15 апреля 2011

вам придется перебирать свой массив и создавать список самостоятельно

// no parameters
var sb = new StringBuilder();
for(int i=0;i<intArray.Length;i++)
{
    sb.Append(intArray[i] + ",");// no SQL injection they are numbers
}
if (sb.Length>0) {sb.Length-=1;}
string sql = "SELECT * FROM table WHERE id IN (" + sb.ToString() + ")";

ОБНОВЛЕНИЕ: Подумав об этом больше, я вернусь к своему первоначальному ответу (ниже), который должен использоватьпараметры.Оптимизация построенных запросов и всего, что может обработать движок базы данных, зависит только от вас.

// no parameters
var sb = new StringBuilder();
for(int i=0;i<intArray.Length;i++)
{
    sb.AppendFormat("p{0},", i);// no SQL injection they are numbers
    connection.Command.Parameters.AddWithValue("p"+i, intArray[i]);
}
if (sb.Length>0) {sb.Length-=1;}
string sql = "SELECT * FROM table WHERE id IN (" + sb.ToString() + ")";
2 голосов
/ 15 апреля 2011

У вас есть несколько вариантов здесь (в порядке предпочтения):

  1. Использовать базу данных, которая поддерживает табличные параметры . Это только способ получить точный синтаксис, который вы хотите.
  2. Данные должны поступать откуда-то: из вашей базы данных, действий пользователя или сгенерированного компьютером источника.

    • Если данные уже есть в вашей базе данных, используйте вместо этого подзапрос.
    • Для других машинно-сгенерированных данных используйте BULK INSERT, SqlBulkCopy или предпочитаемые инструменты массового импорта вашей базы данных.
    • Если он создан пользователем, добавьте его в отдельную таблицу для каждого отдельного действия пользователя , а затем используйте подзапрос.

      Примером этого является корзина для покупок. Пользователь может выбрать несколько предметов для покупки. Вместо того, чтобы хранить их в приложении, и нужно добавлять все элементы в заказ за один раз, когда они оформляются, добавляйте каждый элемент в таблицу в БД, когда пользователь выбирает или изменяет его.

  3. Имейте пользовательскую функцию sql, которая распаковывает строковый параметр в таблицу и возвращает эту таблицу как набор, который вы можете использовать с выражением IN (). См. Связанную статью ниже для более подробной информации о том, как это работает.
  4. Динамически создавать список строк или список параметров на клиенте (как показано в других ответах). Обратите внимание, что это мой минимум предпочтительный вариант.

Окончательная (и я имею в виду окончательная ) работа по этому вопросу находится здесь:

http://www.sommarskog.se/arrays-in-sql.html

Статья длинная, но в хорошем смысле. Автор является экспертом по SQL Server, но в целом концепции применимы и к MySQL.

1 голос
/ 15 апреля 2011

Как я знаю, вы не можете предоставить какой-либо массив в качестве параметра для подготовленного оператора.IN () не поддерживает параметры в виде массива.

0 голосов
/ 28 июля 2015

Ответ от Mud работает только для первого целого в списке параметров.Это означает, что «2,1,3,4» не будет работать, например, если идентификатор равен 1.

См. FIND_IN_SET () против IN () .

Неткомментарий возможен уже сейчас, но также см. ответ от Мэтта Эллена.Редактировал бы его ответ, но не могу.Кажется, что INSTR не работает в случае WHERE с более чем одним идентификатором (возвращает только результат).

Но замена INSTR на LOCATE заставляет его решение работать (с String.Join(",", intArray) в качестве добавленного параметра) ... ГОЛОСОВАТЬ ОТ меня:

LOCATE(CONCAT(',' , CAST(id AS CHAR) , ',') , CONCAT(',' , CAST(@paramter AS CHAR) , ',')) <> 0
0 голосов
/ 16 апреля 2011

Это не работает для огромных списков, но я обнаружил, что only работает, если у вас есть для передачи списка в качестве параметра.

Вместо

SELECT * FROM table WHERE id IN (@parameter)

Вы должны сделать это:

SELECT *
FROM table
WHERE INSTR(','+@parameter+',', ','+CAST(the_column AS CHAR) + ',')

Тогда вы можете перейти в свой список с помощью string.Join(",", intArray)

Это кладж, но это работает.

0 голосов
/ 15 апреля 2011

Я не думаю, что есть способ, которым вы можете добавить их таким образом, но, возможно, вы могли бы перебрать список и динамически сгенерировать запрос.

Например:

var intArray = new List<int>(){1,2,3,4};
if (intArray.Count > 0) {
    var query = "SELECT * FROM table WHERE id IN (";
    for (int i = 0; i < intArray.Count; i++) {
        //Append the parameter to the query
        //Note: I'm not sure if mysql uses "@" but you can replace this if needed
        query += "@num" + i + ",";
        //Add the value to the parameters collection
        ...connection.Command.Parameters.AddWithValue("num" + i, intArray[i]);
    }
    //Remove the last comma and add the closing bracket
    query = query.Substring(0, query.Length - 1) + ");";
    //Execute the query here
}

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

...