如何查找列表/集是否包含在另一个列表中

时间:2012-09-29 10:44:12

标签: mysql sql relational-division

我有一个产品ID列表,我想知道哪些订单包含所有这些产品。订单表的结构如下:

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

有没有希望,还是应该读Tolkien? :)另外,出于好奇,如果在mysql中不可能,是否还有其他具有此功能的数据库?

4 个答案:

答案 0 :(得分:3)

你很亲密

SELECT order_id
FROM orders
WHERE product_id in (222,555) 
GROUP BY order_id
HAVING COUNT(DISTINCT product_id) = 2

关于关系代数中的“出于好奇心”问题,只需使用division即可实现。 AFAIK没有RDBMS实现了任何扩展,使其在SQL中变得简单。

答案 1 :(得分:1)

我倾向于仅在having子句中进行集合比较:

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子句中的一个位置。

答案 2 :(得分:1)

假设您的数据库已正确规范化,即给定订单上没有重复的产品

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

实时测试:http://www.sqlfiddle.com/#!2/fa1ad/5

答案 3 :(得分:1)

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)