В: как я могу получить несколько строк, хотя я использую одно и то же значение несколько раз в предложении 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