使用小组/条件进行反加入

时间:2016-03-28 03:10:23

标签: mysql left-join

注意:我已经简化了问题,因为我认为这个问题和答案都比预期更复杂。

我想要一个反连接,其条件不是第一个表中不存在。

表产品/制造商

  • Widget / Acme
  • Paddle / Acme
  • Ball / Acme
  • Gas / Exxon
  • Pump / Exxon

表:客户/产品

  • Karen / Ball
  • Bob / Paddle
  • Karen / Gas
  • Bob / Pump

A"正常"反加入将找出未通过

订购的产品
Select Products from `Product / Manufacturer` as T1
Left Join `Customer / Product` as T2 
On T2.Zip is NULL 

然而,我所寻找的是哪些客户并没有订购哪些产品,实质上是:

Select Products from `Product / Manufacturer` 
where Manufacturer = 'Acme' that do not exist in `Customer / Product` 
where Customer = 'Karen'

Select Products from `Product / Manufacturer` 
where Manufacturer = 'Exxon' that do not exist in `Customer / Product` 
where Customer = 'Karen'

Select Products from `Product / Manufacturer` 
where Manufacturer = 'Acme' that do not exist in `Customer / Product` 
where Customer = 'Bob'

Select Products from `Product / Manufacturer` 
where Manufacturer = 'Exxon' that do not exist in `Customer / Product` 
where Customer = 'Bob'

' 但作为一个查询,因为有100个"客户"和100多家制造商。

1 个答案:

答案 0 :(得分:1)

如果您想排除制造商的所有产品,而该制造商的产品不会以任何顺序出现......

那意味着您只想包含仅来自某些制造商的产品......

哪些制造商的产品出现在订单中?

SELECT r.manufacturer 
  FROM products r
  JOIN orders s
    ON s.product = r.product
 GROUP BY r.manufacturer 

您可以将该查询包装在parens中并将其包含为内联视图...

SELECT p.*
  FROM ( SELECT r.manufacturer 
           FROM product r
           JOIN orders s
             ON s.product = r.product
          GROUP BY r.manufacturer
       ) q
  JOIN product p 
    ON p.manufacturer = q.manufacturer
  LEFT
  JOIN orders o
    ON o.product = p.Product 
 WHERE o.product IS NULL

还有其他查询模式会返回相同的结果。

<强>后续

注意:原始规范中未明确“按性别/小时划分”部分。

查询模式非常相似。使用内联视图查询为每个性别/小时返回不同的制造商列表。

然后将该集合加入产品表,以获取这些制造商的每个产品。这将包括订购的产品,以及未订购的产品。

然后应用反连接模式,以排除按性别/小时排序的产品。

SELECT q.gender
     , q.hour
     , p.manufacturer
     , p.product
  FROM ( SELECT s.gender
              , s.hour
              , r.manufacturer
           FROM orders s
           JOIN product r
             ON r.product = s.product
          GROUP
             BY s.gender
              , s.hour
              , r.manufacturer
       ) q
  JOIN product p
    ON p.manufacturer = q.manufacturer
  LEFT
  JOIN orders o
    ON o.gender  = q.gender
   AND o.hour    = q.hour
   AND o.product = p.product
 WHERE o.product IS NULL

如果不清楚,请考虑以下查询返回等效集。内联线视图查询t按性别/小时返回制造商的所有产品集。

由于附加的内联视图,此查询效率较低(至少在MySQL中)。虽然时间更长,但可能更容易理解,因为视图查询t明确表示可以返回的所有可能行的集合...按制造商/性别/小时的每个产品。 (要查看该集,可以拉出视图查询t并单独运行以查看它返回的内容。)

在最外面的查询中,t被引用,就像它是一个表一样。如果t被简单的表引用替换,则查询将只是一个简单的反连接。 t中的所有行,不包括匹配的行。

SELECT t.gender
     , t.hour
     , t.manufacturer
     , t.product
  FROM ( 
         SELECT q.gender
              , q.hour
              , q.manufacturer
              , p.product
           FROM ( SELECT s.gender
                       , s.hour
                       , r.manufacturer
                    FROM orders s
                    JOIN product r
                      ON r.product = s.product
                   GROUP
                      BY s.gender
                       , s.hour
                       , r.manufacturer
                ) q
           JOIN product p
             ON p.manufacturer = q.manufacturer
       ) t
  LEFT
  JOIN orders o
    ON o.gender  = t.gender
   AND o.hour    = t.hour
   AND o.product = t.product
 WHERE o.product IS NULL

我建议您先获取返回的行集。在您添加GROUP BYGROUP_CONCAT聚合以折叠行之前。

如果要将“hour”的多个值组合为“am”或“pm”,则可以使用返回“am”或“pm”的表达式(代替“hour”)。 (考虑到该表达式是表中的另一列;但不是引用表中的列,而是使用从表中的其他列派生值的表达式。

   IF(x.hour<12,'am','pm')