Предложение MySQL IN: Как я могу получить несколько строк, когда я использую одно и то же значение несколько раз? - PullRequest
0 голосов
/ 21 июня 2019

Я программирую приложение C # для Windows Forms в Visual Studio и пытаюсь получить данные о ценах на продукты и сумме, которую пользователь добавил в свой список покупок, из моей локальной базы данных MySQL в список ( целое).

Я делаю следующее:

Если пользователь добавил продукт 4 раза в свой список покупок, я добавляю штрих-код продукта 4 раза в свой список (int).

Это работает, но когда я считываю все элементы списка с помощью метода String.Join () в предложении IN моего запроса и выполняю его, он возвращает строку только один раз, хотя IN- оператор имеет один и тот же штрих-код несколько раз.

Вот как я добавляю штрих-коды в свой список (int)

int count = 0;
List<int> barcodes = new List<int>();
MySqlCommand cmd = new MySqlCommand("SELECT product_barcode, amount FROM shopping_list_items WHERE shopping_list_id = " + current_shoppingListID + ";", db.connection);
cmd.ExecuteNonQuery();
var reader = cmd.ExecuteReader();

while (reader.Read())
{
    do
    {
        barcodes.Add(Int32.Parse(reader["product_barcode"].ToString()));
        count++;
    } while (count < Int32.Parse(reader["amount"].ToString()));
}

reader.Close();

Вот как я выполняю свой запрос и присваиваю значения переменным:

MySqlCommand cmdSum = new MySqlCommand("SELECT sum(price) AS 'total', supermarket_id FROM prices WHERE barcode IN (" + String.Join(", ", barcodes) + ") GROUP BY supermarket_id;", db.connection);
cmdSum.ExecuteNonQuery();
var readerSum = cmdSum.ExecuteReader();

while (readerSum.Read())
{
    switch (double.Parse(readerSum["supermarket_id"].ToString()))
    {
        case 1:
            sumSupermarket1 = double.Parse(readerSum["total"].ToString());
            break;

        case 2:
            sumSupermarket2 = double.Parse(readerSum["total"].ToString());
            break;

        case 3:
            sumSupermarket3 = double.Parse(readerSum["total"].ToString());
            break;
     }
}

Упрощенный запрос для простоты может выглядеть так:

SELECT name FROM products WHERE barcode IN (13495, 13495, 13495);

Если приведенный выше мой запрос, я хочу, чтобы он возвращал 3 одинаковые строки.

Итак, теперь мой вопрос: как я могу получить несколько строк, хотя я использую одно и то же значение несколько раз в предложении IN MySQL-запроса?

Ответы [ 2 ]

1 голос
/ 21 июня 2019

В: как я могу получить несколько строк, хотя я использую одно и то же значение несколько раз в предложении IN MySQL-запроса?

A: Мы не делаем. Это не так IN () работает.

Обратите внимание, что

WHERE foo IN ('fee','fi','fi','fi')` 

является сокращением для

WHERE ( foo = 'fee'
     OR foo = 'fi'
     OR foo = 'fi'
     OR foo = 'fi'
      )

Поймите, что здесь происходит. MySQL собирается проверить каждую строку, и для каждой строки он проверяет, возвращает ли это условие ИСТИНА или нет. Если строка удовлетворяет условию, строка возвращается. В противном случае строка не возвращается.

Неважно, что строка со значением foo, равным 'fi', удовлетворяет нескольким условиям. Все, что беспокоит MySQL, - это то, что состояние внутри паренов в конечном итоге оценивается как ИСТИНА.

В качестве иллюстрации рассмотрим:

  WHERE ( t.picked_by     = 'peter piper'
       OR t.picked_amount = 'peck'
       OR t.name          LIKE '%pickled%'
       OR t.name          LIKE '%pepper%'
        )

Может быть строка, которая удовлетворяет каждому из этих условий. Но предложение WHERE только спрашивает, оценивает ли все условие значение ИСТИНА. Если это так, верните строку. Если это не так, исключите строку. Мы не получаем четыре копии строки, потому что выполняется более одного условия.


Итак, как нам получить набор с несколькими копиями строки?

Как один из возможных вариантов, мы могли бы использовать отдельные операторы SELECT и объединить результаты с UNION ALL оператором set. Примерно так:

SELECT p1.name FROM product p1 WHERE p1.barcode IN (13495)
UNION ALL
SELECT p2.name FROM product p2 WHERE p2.barcode IN (13495)
UNION ALL
SELECT p3.name FROM product p3 WHERE p3.barcode IN (13495)

Обратите внимание, что результат этого запроса значительно отличается от результата исходного запроса.

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


Followup

Без понимания варианта использования, спецификации, я просто догадываюсь, чего мы пытаемся достичь. На основании двух запросов, показанных в коде (который следует общему шаблону, который мы видим в коде, уязвимом для SQL-инъекций),

Список покупок:

SELECT i.product_barcode
     , i.amount
  FROM shopping_list_item i
 WHERE i.shopping_list_id = :id

Что такое amount? Это заказанное количество? Мы хотим две банки этого или три фунта этого? Похоже, мы хотели бы умножить цену за единицу на количество, заказанное, чтобы получить стоимость. (Две банки будут стоить в два раза больше, чем одна.)

Если то, что мы ищем, это общая стоимость товаров в списке покупок из нескольких магазинов, мы можем сделать что-то вроде этого:

SELECT SUM(p.price * s.amount)  AS `total`
     , p.supermarket_id 
  FROM ( SELECT i.product_barcode
              , i.amount
           FROM shopping_list_item i 
          WHERE i.shopping_list_id = :id
       ) s
  JOIN price p
    ON p.barcode = s.product_barcode
 GROUP
    BY p.supermarket_id

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

Для повышения производительности мы можем исключить встроенное представление и написать запрос следующим образом:

SELECT SUM(p.price * i.amount)  AS `total`
     , p.supermarket_id
  FROM shopping_list_item i
  JOIN price p
    ON p.barcode = i.product_barcode
 WHERE i.shopping_list_id = :id
 GROUP
    BY p.supermarket_id

Если нам абсолютно необходимо просмотреть запрос списка покупок, а затем использовать строки из него для создания второго запроса, мы могли бы сформировать запрос, который выглядит примерно так:

SELECT SUM(p.price * i.amount) AS `total`
     , p.supermarket_id 
  FROM ( -- shopping_list here
         SELECT '13495' AS product_barcode, '1'+0 AS amount 
         UNION ALL SELECT '13495', '1'+0
         UNION ALL SELECT '13495', '1'+0
         UNION ALL SELECT '12222', '2'+0
         UNION ALL SELECT '15555', '5'+0
         -- end shopping_list
       ) i
   JOIN price p
     ON p.barcode = i.product_barcode
  WHERE i.shopping_list_id = :id
  GROUP
     BY p.supermarket_id
0 голосов
/ 22 июня 2019

Возможно, вам лучше было бы исследовать LINQ to SQL, а не использовать прямой SQL и внедрение.

Вы можете использовать объединение встроенных таблиц, чтобы выполнить то, что вы хотите:

"SELECT sum(price) AS 'total', supermarket_id
 FROM (select "+barcodes[0]+"as bc union all select "+String.Join(" union all select ", barcodes.Skip(1).ToArray())+") w
 JOIN prices p ON p.barcode = w.bc
 GROUP BY supermarket_id;"

Примечание: Если вы можете назвать столбец псевдонимом встроенной таблицы (я не смог проверить это), вы могли бы упростить генерацию встроенной таблицы.

...