【问题标题】:How to find if a list/set is contained within another list如何查找列表/集合是否包含在另一个列表中
【发布时间】:2012-09-29 10:44:11
【问题描述】:

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

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 中不可能,是否有任何其他数据库具有此功能?

【问题讨论】:

  • 这是字符串类型的编程:(222,555) IN GROUP_CONCAT(product_id),通常不鼓励使用。使用正确的基于集合的方法
  • 气馁?我认为这甚至不会执行,更不用说产生任何有意义的结果。我相信我已经很清楚这是一个“幻想”查询,只是为了更好地了解我在寻找什么。
  • 是的,我知道它甚至不是一个有效的结构。我明白了,我只是以为你真的想使用 group_concat ;-)
  • 哦,我当然想使用 group_concat :)
  • 这就是我所说的字符串类型编程 :-) 避免通过字符串方法(例如group_concat)处理事物,尤其是当基于集合的正确方法可用时。字符串类型编程:stackoverflow.com/a/11846552 基于集合的正确编程:stackoverflow.com/a/11846457

标签: mysql sql relational-division


【解决方案1】:

你很亲密

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 中变得如此简单的扩展。

【讨论】:

    【解决方案2】:

    我倾向于只在have子句中进行集合比较:

    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(只需将“and”更改为“or”)。或者,333 和 555 或 222 没有 555。

    其次,当您创建查询时,您只需将条件放在一个位置,即having 子句中。

    【讨论】:

      【解决方案3】:

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

      mysql主义:

      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

      【讨论】:

        【解决方案4】:
        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)
        

        【讨论】:

        • 我相信你误解了我的问题。请看其他答案。
        • 这列出了包含所有产品的订单。如果这不是你想要的,你应该改写你的问题,恕我直言。 (而且我认为我不需要阅读答案即可了解问题的含义)
        • 我有一个列表的产品。每次我运行查询时,该列表可能会更改。您的查询似乎在做的是对照 full products 表检查 orders 表。你当然不需要阅读任何东西。只是您花了时间回答这个问题,并认为我会指出它为什么不正确。还是谢谢你。
        • 数据库没有列表。这就是我将产品列表放入产品 的原因。你没有提供表格,所以我自己做了一个(使用订单表中的所有现有产品)。
        • 好的,对不起,如果我昨天行动太快了。我会赞成你似乎是一个正确的答案,并迫使我阅读 (NOT) EXISTS 但与其他建议相比,这仍然看起来太复杂且难以阅读。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-11-21
        • 2017-01-13
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多