简体   繁体   English

如何编写SQL查询以检查一个表上的多对多关系是否是另一个表上多对多关系的子集?

[英]How do I write a SQL query to check if a many-to-many relationship on one table is a subset of a many-to-many relationship on another table?

I have some models as follows: 我有一些模型如下:

Product:
  id
  name
  price

Order:
  id
  user
  created

OrderProduct:
  order_id
  product_id

Promo:
  id
  discount

PromoProduct:
  promo_id
  product_id

A promotion applies to an order if all products in the promotion are present in the order. 如果促销中的所有产品都存在于订单中,则促销适用于订单。 How do I write a SQL query to find out what promotions apply to an order? 如何编写SQL查询以查找适用于订单的促销活动?

SELECT  *
FROM    promo p
WHERE   id NOT IN
        (
        SELECT  pp.promo_id
        FROM    promoProduct pp
        WHERE   pp.product_id NOT IN
                (
                SELECT  product_id
                FROM    orderProduct op
                WHERE   op.order_id = $order
                )
        )

Note that this will also return all promotions with empty product lists ("there ain't no plane in this hangar that I can't fly"). 请注意,这也会返回所有带有空产品列表的促销活动(“这个机库中没有我无法飞行的飞机”)。 If that's a problem, you will need to throw in an additional check for non-emptiness of the promotions. 如果这是一个问题,您需要额外检查促销的非空虚。

This should fulfil your criterion that all the products in the promo need to be part of the order - because of the q1.ct = q2.ct . 这应该符合您的标准,即促销中的所有产品需要成为订单的一部分 - 因为q1.ct = q2.ct q1.ct gives you a count of products per promo per order; q1.ct为您提供每个订单每个促销的产品数量; q2.ct gives you a total count of products per order. q2.ct为您提供每个订单的产品总数。

Obviously you can further join this to the Order and Promo tables to get whatever additional information you want in your result set. 显然,您可以进一步将其加入订单和促销表,以获得您在结果集中所需的任何其他信息。

SELECT q1.order_id, q1.promo_id FROM
( SELECT op.order_id, pp.promo_id, COUNT( op.product_id ) AS ct
FROM OrderProduct op INNER JOIN PromoProduct pp ON product_id
GROUP BY op.order_id, pp.promo_id ) q1

INNER JOIN

( SELECT pp.promo_id, COUNT( pp.product_id ) AS ct
FROM PromoProduct pp
GROUP BY pp.promo_id ) q2

ON q1.promo_id = q2.promo_id AND q1.ct = q2.ct

Using subqueries you can divide and conquer. 使用子查询可以分而治之。

Create a query to find how many products are in each promo: 创建一个查询以查找每个促销中有多少产品:

SELECT promo_id, COUNT(product_id) AS no_products
FROM PromoProduct
GROUP BY promo_id

Then create another to find how many products for each order are in a promo: 然后创建另一个以查找促销中每个订单的产品数量:

SELECT OP.order_id, PP.promo_id, COUNT(OP.product_id) AS no_products
FROM OrderProduct OP
INNER JOIN PromoProduct PP ON OP.product_id = PP.product_id
GROUP BY OP.order_id, PP.promo_id

Then join them together: 然后加入他们:

SELECT OP.order_id, PP.promo_id
FROM OrderProduct OP
INNER JOIN PromoProduct PP ON OP.product_id = PP.product_id
INNER JOIN (SELECT promo_id, COUNT(product_id) AS no_products
FROM PromoProduct
GROUP BY promo_id) PC ON PP.promo_id = PC.promo_id AND COUNT(OP.product_id) = PC.no_products
GROUP BY OP.order_id, PP.promo_id

That may need a little tweaking but you get the point. 这可能需要一些调整,但你明白了。

You could left join a given promo's products to a given order's products, and if any orderproducts row(s) came back null it would mean that the order did not qualify for that promo, given your definition of what it takes to qualify for a promo. 您可以将给定促销产品加入给定订单的产品,如果任何订单产品行返回null,则意味着该订单不符合该促销的条件,因为您定义了获得促销资格所需的条件。

EDIT: 编辑:

        select promoproducts.productid, orderproducts.productid
        from promoproducts
        left join orderproducts
        on promoproducts.productid = orderproducts.productid
        where promoid = 'x' and orderid = 'y'

EDIT2: ignore above (see Quassnoi's comment); EDIT2:忽略上面(见Quassnoi的评论); the approach I'm suggesting needs to be done as a LEFT JOIN to an inline view: 我建议的方法需要作为内联视图的LEFT JOIN来完成:

        select pp.productid as promoproduct, orderproduct
        from promoproducts pp
        left join 

          (
         select productid as orderproduct
           from orderproducts
           where orderid=1

         ) as op
         on pp.productid=op.orderproduct
          where promoid =1

EDIT3: instead of comparing the order to a specific promo ( where promoid = x ), it could be compared against all current promos: EDIT3:不是将订单与特定促销( where promoid = x )进行比较,而是将其与所有当前促销进行比较:

          ...  where promoid in (select promoid from promos where startdate ... expirydate ...) 
           -- find current promos

The real-world advantage to this left-join approach is that it shows you all of the promos the order qualifies for as well as all of the promos it does not qualify for and which product(s) would have to be added to the order to qualify for a promo . 这种左连接方法的真实优势在于它向您显示订单所符合的所有促销,以及它不符合条件的所有促销以及必须将哪些产品添加到订单中有资格获得促销

SELECT *
FROM 'order' o
INNER JOIN orderproduct op ON op.order_id = o.order_id
INNER JOIN PromoProduct p ON p.product_id = op.product_id
WHERE o.order_id = ?

Try that.. Though I think you should rename the order table, as it is a keyword in MySQL. 试试..虽然我认为你应该重命名订单表,因为它是MySQL中的关键字。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM