【问题标题】:MySQL excluding results on a JOINMySQL 排除 JOIN 上的结果
【发布时间】:2011-11-18 20:26:22
【问题描述】:

我正在开发一个餐厅 CMS 应用。我在 2 个表 menu_sectionsmenu_items 之间有一个多对多的关系。这种关系是通过一个名为 menu_relationships 的表来维护的。

例如,假设名为 Snacks (menu_section_id = 1) 的菜单部分包含名为 Pretzels (menu_item_id = 1) 的菜单项和名为 的菜单部分>甜点 (menu_section_id = 2) 包含一个名为Ice Cream (menu_item_id = 2) 的菜单项,但Ice Cream 也包含在另一个名为儿童食品 (menu_section_id = 3)。因此,menu_relationships 表中将有 3 行来映射这 3 个关系。关系表如下所示:

---------------------------------------
|   menu_section_id  |  menu_item_id  |
|=====================================|
|          1         |        1       |
|-------------------------------------|
|          2         |        2       |
|-------------------------------------|
|          3         |        2       |
---------------------------------------

到目前为止一切顺利。

我想生成一个结果集,它将返回所有菜单项的名称,但具有给定menu_section_id 的菜单项除外。所以要返回菜单项名称,我在menu_items 表上有一个连接。这是 SQL:

SELECT menu_section_id, menu_items.menu_item_id, menu_item_name 
FROM menu_relationships
JOIN menu_items 
ON menu_items.menu_item_id = menu_relationships.menu_item_id
WHERE menu_section_id != 2 

结果集将为不包含给定 menu_section_id 的每个关系提供一行。使用示例数据,我将从关系表中返回 2 行:

-----------------------------------------------------------
|   menu_section_id   |  menu_item_id  |  menu_item_name  |
|======================================|==================|
|          1          |        1       |     Pretzels     |
|--------------------------------------|------------------|
|          3          |        2       |     Ice Cream    |
-----------------------------------------------------------

但我想要的是从结果集中完全排除菜单项,如果它与指定的menu_section_id 有任何关系。换句话说,在此示例中,我只想将完全没有关系映射的菜单项的行返回到 2 的 menu_section_id,我只想返回 Pretzels 行.

我已经使用 bit_xor() 聚合函数对 GROUP BYHAVING 进行了各种尝试,但到目前为止,我完全没有得到我想要的东西。

我可能花更少的时间来解释这一点,但我希望它尽可能清楚。我希望是的。任何人都可以帮忙吗?

【问题讨论】:

  • 我想我对这么长的解释感到更加困惑。任何人都听说过少即是多。我读了这个,不确定你在找什么。只需向我们提供您想要的数据和结果。阅读椒盐卷饼冰淇淋汉堡和热狗只会让我感到饥饿。
  • @JonH -- 为什么不提供没有讽刺意味的建设性建议呢?这显然是一个新用户,可能还不了解 SO 的来龙去脉,或者可能不习惯在这样的论坛中表达自己。
  • 对不起,JonH,我决定我宁愿太罗嗦也不愿太含糊。下次我会切入正题,并在需要时添加额外的信息/示例。

标签: mysql join group-by


【解决方案1】:

这是使用 LEFT OUTER JOIN 的绝佳案例,因为它包含左侧表格中的所有行并尽可能匹配,如果不匹配则返回 NULL

基于上面的 Mark Breyer 示例查询,请参见以下示例:

SELECT R.menu_section_id, I.menu_item_id, I.menu_item_name
FROM menu_items AS I
LEFT OUTER JOIN menu_relationships R on (R.menu_item_id=I.menu_item_id) AND (R.menu_section_id = 2)

mysql 优化器实际上可能会将其重写为子查询——无论如何我都不是优化专家——我会看看你的索引是如何构建的,看看这种类型的连接是否对你的架构有意义.我还会测试它是否真的更快,因为它实际上不那么语义化。

【讨论】:

  • @jemminger 就像一个坏便士
【解决方案2】:

有很多方法可以做到这一点。这是一个使用WHERE ... NOT IN (...)的例子:

SELECT
    R.menu_section_id,
    I.menu_item_id,
    I.menu_item_name
FROM menu_items AS I
JOIN menu_relationships AS R
ON R.menu_item_id = I.menu_item_id
WHERE I.menu_item_id NOT IN
(
     SELECT menu_item_id  
     FROM menu_relationships
     WHERE menu_section_id = 2 
)

【讨论】:

    【解决方案3】:

    我会为此使用一个子查询,获取每个 menu_item_id 的 menu_section_id 为 2,然后使用 NOT IN。给你:

    SELECT menu_section_id, menu_items.menu_item_id, menu_item_name 
    FROM menu_relationships
    JOIN menu_items 
    ON menu_items.menu_item_id = menu_relationships.menu_item_id
    WHERE menu_relationships.menu_item_id NOT IN (
                                                  SELECT menu_item_id 
                                                  FROM menu_relationships 
                                                  WHERE menu_section_id = 2
                                                 );
    

    【讨论】:

      【解决方案4】:

      我本来打算很好地建议一个子查询,但我想提一下子查询会极大地影响您网站的性能。您可能需要考虑缓存选项,以避免由于此类原因导致严重的加载时间挂起。

      在大多数情况下,您会没事的,但如果您只是向我们展示问题的一部分,而没有提及不相关的细节,那么您很可能正在构建一个网站,在一个页面上运行 100 个此类查询,例如,因为有人在这里提到它而没有提到复合开销,这样的事情可能会导致......

      就像我说的,你可能会没事的。除非您想重新启动服务器,否则不要在子查询中执行子查询。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2016-01-02
        • 1970-01-01
        • 2016-10-07
        • 2013-10-07
        • 2015-08-15
        • 2014-11-09
        • 1970-01-01
        相关资源
        最近更新 更多