MySQL就地两个左连接之间的MINUS

时间:2014-03-05 02:39:08

标签: mysql left-join

我熟悉“LEFT JOIN / IS NULL”这个习惯用法,以获得相当于MySQL中的MINUS运算符。尽管如此,我一直试图解决这个问题一段时间没有成功(没有丑陋的子选择)

以下是一个示例结果集来说明问题:

+-------------------+----------------------------------------------+-----------+
| group_id          | valid                                        | not_valid |
+-------------------+----------------------------------------------+-----------+
| favorites         | AD12,AD17,AD10,AD15,AD13,AD18,AD11,AD16,AD14 | NULL      |
| fruits_veggies    | AD13                                         | NULL      |
| pizza_grill       | AD12,AD10,AD21,AD19,AD11,AD22,AD20           | NULL      |
| salsa_wraps       | NULL                                         | NULL      |
| student_beverages | AD32,AD30,AD31                               | AD31,AD30 |
+-------------------+----------------------------------------------+-----------+

在上面,我想删除“not_valid”中也存在的“valid”列中的任何值,因此AD31,AD30应该消失,只留下AD32,所以经典的MINUS运算符:)

这是给出上述结果集的SQL。知道如何扩展它以消除有效SKU中的所有not_valid SKU吗?

select
    gm.group_id,
    group_concat(pt_include.sku) valid,
    group_concat(pt_exclude.sku) not_valid

from main_menu mm

    left join group_membership gm on 
        mm.master_account_id = gm.master_account_id
        and mm.group_id = gm.group_id

    left join product_tag pt_include on 
        mm.master_account_id = pt_include.master_account_id
        and gm.tag = pt_include.tag and gm.inclusive = '+'

    left join product_tag pt_exclude on 
        mm.master_account_id = pt_exclude.master_account_id
        and gm.tag = pt_exclude.tag and gm.inclusive = '-'

where 
    mm.master_account_id = 321
    and mm.menu_id = 987

group by gm.group_id

跟进:

下面我为了简洁而删除了所有其他数据。考虑产品可以标记为X,Y,Z。目标是取回标记为“X”和“X”的项目。 “Y”,但不是“Z”。可以有任意数量的“包含”或“独占”标签。用户输入类似+ X,+ Y,-Z。

的内容

在我们的示例中,我们想要取回所有标记为饮料的产品,但如果饮料是'teacher_only'则排除。因此,这两个连接代表了这两组:首先加入所有饮料,第二加入所有饮料和饮料。 teacher_only。最终结果应该是首先加入MINUS第二次加入。

mysql> select * from main_menu;
+-------------------+------------------------+-------------------+
| master_account_id | menu_id                | group_id          |
+-------------------+------------------------+-------------------+
| FA32113145        | 1231                   | student_beverages |
+-------------------+------------------------+-------------------+

mysql> select * from group_membership;
+-------------------+-------------------+--------------+-----------+
| master_account_id | group_id          | tag          | inclusive |
+-------------------+-------------------+--------------+-----------+
| FA32113145        | student_beverages | beverage     | +         |
| FA32113145        | student_beverages | teacher_only | -         |
+-------------------+-------------------+--------------+-----------+

mysql> select * from product_tag;
+-------------------+------+--------------+
| master_account_id | sku  | tag          |
+-------------------+------+--------------+
| FA32113145        | AD30 | beverage     |
| FA32113145        | AD30 | teacher_only |
| FA32113145        | AD31 | beverage     |
| FA32113145        | AD31 | teacher_only |
| FA32113145        | AD32 | beverage     |
+-------------------+------+--------------+

1 个答案:

答案 0 :(得分:0)

您不需要两个连接。只需使用条件聚合。 group_concat()将忽略NULL的参数,因此您只需使用case语句即可:

select gm.group_id,
       group_concat(case when gm.inclusive = '+' then pt.sku end) as valid,
       group_concat(case when gm.inclusive = '-' then pt.sku end) as not_valid
from main_menu mm left join
     group_membership gm
     on mm.master_account_id = gm.master_account_id and
        mm.group_id = gm.group_id left join
     product_tag pt
     on mm.master_account_id = pt.master_account_id and
        gm.tag = pt.tag and gm.inclusive = '+'
where mm.master_account_id = 321 and
      mm.menu_id = 987
group by gm.group_id;

编辑:

哦,问题是您有两种类型的代码-+都有代码,在这种情况下,您只需要-列中的代码。我会先通过sku汇总,然后再按小组汇总:

select gm.group_id,
       group_concat(case when includeplus = 1 and includeminus = 0 then sku end) as valid,
       group_concat(case when includeminus = 1 then sku end) as invalid
from (select gm.group_id, pt.sku,
             max(gm.include = '+') then includeplus,
             max(gm.include = '-') then includeminus
      from main_menu mm left join
           group_membership gm
           on mm.master_account_id = gm.master_account_id and
              mm.group_id = gm.group_id left join
           product_tag pt
           on mm.master_account_id = pt.master_account_id and
              gm.tag = pt.tag and gm.inclusive = '+'
      where mm.master_account_id = 321 and
            mm.menu_id = 987
      group by gm.group_id, pt.sku
     ) g
group by gm.group_id;