Как найти, если список / набор содержится в другом списке
-
11-12-2019 - |
Вопрос
У меня есть список идентификаторов продукта, и я хочу выяснить, какие заказы содержат все эти продукты.Таблица заказов структурирована так:
order_id | product_id
----------------------
1 | 222
1 | 555
2 | 333
.
Очевидно, я могу сделать это с некоторыми цикловными в PHP, но мне было интересно, если есть элегантный способ сделать это чисто в MySQL. Мой идеальный фантастический запрос был бы чем-то вроде:
SELECT order_id
FROM orders
WHERE (222,555) IN GROUP_CONCAT(product_id)
GROUP BY order_id
.
Есть ли какие-нибудь надежда или я должен пойти прочитать Толькиен?:) Кроме того, из любопытства, если невозможно в MySQL, есть ли другая база данных, которая имеет эту функциональность?
Решение
Вы были закрыты
SELECT order_id
FROM orders
WHERE product_id in (222,555)
GROUP BY order_id
HAVING COUNT(DISTINCT product_id) = 2
.
Что касается вопроса вашего «из любопытства» в реляционной алгебре. Это достигается просто с Разделение .AFAIK Нет RDBMS внедрил любое расширение, которое делает это как просто в SQL.
Другие советы
У меня есть предпочтение для выполнения заданных сравнений только в пункте имеющих:
select order_id
from orders
group by order_id
having sum(case when product_id = 222 then 1 else 0 end) > 0 and
sum(case when product_id = 555 then 1 else 0 end) > 0
.
Что это говорит: получить все заказы, где заказ имеет хотя бы один продукт 222 и хотя бы один продукт 555.
Я предпочитаю это по двум причинам.Первая - обобщенность.Вы можете организовать более сложные условия, такие как 222 или 555 (просто изменив «и» и «или» или «).Или 333 и 555 или 222 без 555.
Во-вторых, когда вы создаете запрос, вам нужно только поместить условие в одном месте, в пункте having
.
Предполагая, что ваша база данных нормализована правильно, то есть нет дубликата продукта на данном порядке
mysqlism:
select order_id
from orders
group by order_id
having sum(product_id in (222,555)) = 2
.
Стандартный SQL:
select order_id
from orders
group by order_id
having sum(case when product_id in (222,555) then 1 end) = 2
.
Если он имеет дубликаты:
CREATE TABLE tbl
(`order_id` int, `product_id` int)
;
INSERT INTO tbl
(`order_id`, `product_id`)
VALUES
(1, 222),
(1, 555),
(2, 333),
(1, 555)
;
.
Сделайте это тогда:
select order_id
from tbl
group by order_id
having count(distinct case when product_id in (222,555) then product_id end) = 2
.
Live Test: http://www.sqlfiddle.com/#!2/fa1ad/ 5
CREATE TABLE orders
( order_id INTEGER NOT NULL
, product_id INTEGER NOT NULL
);
INSERT INTO orders(order_id,product_id) VALUES
(1, 222 ) , (1, 555 ) , (2, 333 )
, (3, 222 ) , (3, 555 ) , (3, 333 ); -- order#3 has all the products
CREATE TABLE products AS (SELECT DISTINCT product_id FROM orders);
SELECT *
FROM orders o1
--
-- There should not exist a product
-- that is not part of our order.
--
WHERE NOT EXISTS (
SELECT *
FROM products pr
WHERE 1=1
-- extra clause: only want producs from a literal list
AND pr.product_id IN (222,555,333)
-- ... that is not part of our order...
AND NOT EXISTS ( SELECT *
FROM orders o2
WHERE o2.product_id = pr.product_id
AND o2.order_id = o1.order_id
)
);
.
Результат:
order_id | product_id
----------+------------
3 | 222
3 | 555
3 | 333
(3 rows)
.