我熟悉“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 |
+-------------------+------+--------------+
答案 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;