【问题标题】:SQL: Get Products from a category but also must be in another set of categoriesSQL:从一个类别中获取产品,但也必须在另一组类别中
【发布时间】:2013-07-13 19:28:03
【问题描述】:

我目前陷入困境。场景是这样的。我有可能与多个类别相关联的产品。数据结构如下图:

Products Table:
product_id  name 
1           Lemon
2           Kiwis
3           Cheese

产品分类表

product_id   category_id
1            1
1            2
1            3
2            1
2            3
3            2
3            4

类别表(查询中不需要,但在此处添加以帮助可视化正在发生的事情)

category_id  name
1            Fruit
2            Yellow
3            Round
4            Dairy

我在这里苦苦挣扎的是,最初我想获得水果类别(类别 id 1)中的所有产品,但我还想检查水果是否是黄色的。请记住,黄色不会是唯一的过滤器,有时我会想要返回黄色和橙色的水果,但是由于奶酪是黄色的,我不能返回它,因为它不是水果。然而,为了让事情变得更容易一些,我始终知道我将以水果类别为基础。

数据库结构不能像opencart数据库结构那样改变。

这是我的尝试:

SELECT GROUP_CONCAT(DISTINCT p2c2.category_id SEPARATOR ',') as categories 
FROM oc_product_to_category p2c 
LEFT JOIN oc_product p ON (p.product_id = p2c.product_id) 
LEFT JOIN oc_product_to_category p2c2 ON (p.product_id = p2c2.product_id) 
WHERE p2c.category_id IN ('1','2')

除了会返回 Cheese 之外,这种方法有效。

注意:我使用 Group Concat 是因为在所有这一切结束时,我的目标不是返回与这些类别匹配的产品,而是基于过滤器,我想从匹配的产品中返回另一个类别列表标准。所以:

场景:

获取符合类别条件的产品 返回这些产品的类别。

我们将不胜感激。

【问题讨论】:

    标签: mysql left-join group-concat relational-division


    【解决方案1】:

    这种类型的问题称为关系划分

    有两种常见的解决方案:

    1. 第一个解决方案将匹配的类别串在一起并与固定字符串进行比较:

      SELECT p2c.product_id
      FROM oc_product_to_category p2c
      GROUP BY p2c.product_id
      HAVING GROUP_CONCAT(p2c.category_id SEPARATOR ',' ORDER BY p2c.category_id) = '1,2'
      
    2. 第二个解决方案为每个所需值执行JOIN

      SELECT p.product_id 
      FROM oc_product p 
      INNER JOIN oc_product_to_category p2c1 
        ON (p.product_id = p2c1.product_id AND p2c1.category_id = 1) 
      INNER JOIN oc_product_to_category p2c2 
        ON (p.product_id = p2c2.product_id AND p2c2.category_id = 2) 
      

    我在我的演示文稿SQL Query Patterns, Optimized 中介绍了这些解决方案。我在测试中发现加入解决方案的性能要好得多。


    @Tom 的建议是正确的,这是完整查询中的样子:

        SELECT p.product_id, GROUP_CONCAT(p2c3.category_id SEPARATOR ',') AS categories
        FROM oc_product p 
        INNER JOIN oc_product_to_category p2c1 
          ON (p.product_id = p2c1.product_id AND p2c1.category_id = 1) 
        INNER JOIN oc_product_to_category p2c2 
          ON (p.product_id = p2c2.product_id AND p2c2.category_id = 2) 
        INNER JOIN oc_product_to_category p2c3
          ON (p.product_id = p2c3.product_id)
        GROUP BY p.product_id;
    

    @Tom 建议的 DISTINCT 不是必需的,因为您的 p2c 表应该对 (product_id, category_id) 具有唯一约束。

    【讨论】:

    • 嗨,比尔,首先感谢您抽出宝贵时间,我假设这些只是返回具有这些类别 ID 的产品。问题的第二部分根据返回的产品 id 询问我能否获得该产品的所有类别?
    • ^ 在同一个查询中 (sry) 因为我有很多产品并且在 2 个查询中执行此操作,我相信这会给服务器带来很大压力。
    • 如果我是你,我会采用 Bill 的第二个解决方案,向 oc_product_to_category 表添加一个单独的连接,然后将选择更改为您在查询中的内容:SELECT GROUP_CONCAT(DISTINCT p2c.category_id SEPARATOR ',') as categories
    • 嗯,第一个解决方案要求产品有没有其他类别然后搜索的那些(在这种情况下:它必须有'水果'和 'Yellow',但 not 也有 'Sweet')。我相信第二个更快,但是,省略 WHERE category_id IN(...) 会使第一个更慢,而且不正确。
    • Fwiw:如果我将第一个解决方案从 GROUP BY p2c.product_id HAVING GROUP_CONCAT(p2c.category_id SEPARATOR ',' ORDER BY p2c.category_id) = '1,2' 重写为 WHERE p2c.category_id IN (1,2) GROUP BY p2c.product_id HAVING COUNT(p2c.category_id)=2,它相对于第二个选项 (1) 的得分相当一致,但在 1 类别中是随机的(对于当然不应该使用它;)),(2)在2类别(3)在3类别时差约10%(4)在9类别时差约50%,和它的踪迹。所以是的,第二个更好,有多个类别。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-27
    相关资源
    最近更新 更多