1

给定以下(非常简化的)mysql表结构:

产品

  • ID

产品类别

  • ID
  • product_id
  • 状态(整数)

产品标签

  • ID
  • product_id
  • some_other_numeric_value

我试图找到与某个 product_tag 相关联的每个产品,并且与至少一个 status-attribute 为 1 的类别相关联。

我尝试了以下查询:

SELECT *

FROM `product` p

JOIN `product_categories` pc
ON p.`product_id` = pc.`product_id`

JOIN `product_tags` pt
ON p.`product_id` = pt.`product_id`

WHERE pt.`some_value` = 'some comparison value'

GROUP BY p.`product_id`

HAVING SUM( pc.`status` ) > 0

ORDER BY SUM( pt.`some_other_numeric_value` ) DESC

现在我的问题是:SUM(pt.some_other_numeric_value)返回意外的值。

我意识到,如果有问题的产品与 product_categories 表的关系不止一个,那么与product_tags的每个关系都被计算为与product_categories表的关系一样多的时间!

例如:如果 id=1 的产品与 ids = 2、3 和 4 的 product_categories 有关系,并且与 id 为 5 和 6 的 product_tags 有关系 - 那么如果我插入 a GROUP_CONCAT(pt.id),那么它会给出5,6,5 ,6,5,6而不是预期的5,6

起初我怀疑是连接类型(左连接、右连接、内连接等)有问题,所以我尝试了所有我知道的连接类型,但无济于事。我还尝试在GROUP BY子句中包含更多的 id 字段,但这也没有解决问题。

有人可以向我解释这里到底出了什么问题吗?

4

2 回答 2

5

您通过关系将“主” ( product) 表连接到两个表 (tags和) ,因此这是意料之中的,您正在创建一个迷你笛卡尔积。对于同时具有多个关联标签和多个关联类别的产品,将在结果集中创建多行。如果您分组依据,您在聚合函数中会得到错误的结果。categories1:n


避免这种情况的一种方法是删除两个连接之一,如果您不需要该表的结果,这是一个有效的策略。假设您不需要表中SELECT列表中的任何内容product_categories。然后您可以使用半联接(该EXISTS subquery)表:

SELECT p.*,
       SUM( pt.`some_other_numeric_value` )

FROM `product` p

JOIN `product_tags` pt
  ON p.`product_id` = pt.`product_id`

WHERE pt.`some_value` = 'some comparison value'

  AND EXISTS
      ( SELECT *
        FROM product_categories pc
        WHERE pc.product_id = pc.product_id
         AND  pc.status = 1
      ) 

GROUP BY p.`product_id`

ORDER BY SUM( pt.`some_other_numeric_value` ) DESC ;

规避此问题的另一种方法是 - 在 - 之后GROUP BY MainTable.pk- 在DISTINCT内部COUNT()GROUP_CONCAT()聚合函数中使用。这有效,但您不能将其与SUM(). 因此,它在您的特定查询中没有用。


第三种选择 - 始终有效 - 是首先按两个(或更多)边表分组,然后加入主表。在你的情况下是这样的:

SELECT p.* ,
       COALESCE(pt.sum_other_values, 0) AS sum_other_values
       COALESCE(pt.cnt, 0) AS tags_count,
       COALESCE(pc.cnt, 0) AS categories_count,
       COALESCE(category_titles, '') AS category_titles

FROM `product` p

JOIN 
    ( SELECT product_id
           , COUNT(*) AS cnt
           , GROUP_CONCAT(title) AS category_titles
      FROM `product_categories` pc
      WHERE status = 1
      GROUP BY product_id
    ) AS pc
  ON p.`product_id` = pc.`product_id`

JOIN 
    ( SELECT product_id
           , COUNT(*) AS cnt
           , SUM(some_other_numeric_value) AS sum_other_values
      FROM `product_tags` pt
      WHERE some_value = 'some comparison value'
      GROUP BY product_id
    ) AS pt
ON p.`product_id` = pt.`product_id`

ORDER BY sum_other_values DESC ;

COALESCE()那里并不严格需要 - 以防万一您将内部连接更改为外部LEFT连接。

于 2013-01-03T14:30:43.073 回答
0

你不能通过求和函数排序

相反,您可以这样做

 SELECT * ,SUM( pt.`some_other_numeric_value` ) as sumvalues

 FROM `product` p

 JOIN `product_categories` pc
 ON p.`product_id` = pc.`product_id`

 JOIN `product_tags` pt
 ON p.`product_id` = pt.`product_id`

 WHERE pt.`some_value` = 'some comparison value'

 GROUP BY p.`product_id`

 HAVING SUM( pc.`status` ) > 0

 ORDER BY sumvalues DESC
于 2013-01-03T14:21:04.610 回答