mysql的聚合函数查询两个联接给人意外的结果(mysql aggregate functions

2019-10-17 12:58发布

鉴于以下(非常简单的)mysql的表结构:

制品

  • ID

产品类别

  • ID
  • PRODUCT_ID
  • 状态(整数)

product_tags

  • ID
  • PRODUCT_ID
  • some_other_numeric_value

我试图找到每一个有关联到一定product_tag产品,而且关系到至少一个类别伟驰状态属性为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,以及与ID 5及ID 6的product_tags的关系的关系product_categories -然后,如果我插入GROUP_CONCAT(pt.id)则它给5,6,5,6,5,6而不是预期的5,6。

起初我怀疑它是与连接类型(左连接,右连接,内连接,等等)的一个问题,所以我想尽连接类型,我知道的,但无济于事。 我也试图包括更多的ID字段到GROUP BY子句中,但这种didn't要么解决问题。

谁能给我解释一下什么是真正去错在这里?

Answer 1:

你加入一个“主”( product )表两个表( tagscategories通过) 1:n的关系,所以这是预期的,要创建一个小型笛卡尔乘积。 对于那些同时拥有一个以上的相关标签和一个以上的相关产品类别,在结果集中创建了多个行。 如果按,你必须在聚合函数错误的结果。


为了避免这种情况的一个方法是删除这两个中的一个连接,这是一个有效startegy如果你不需要从该表中的结果。 说你不需要在任何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()并非严格需要有-万一你chnage内加入到LEFT外连接。



Answer 2:

你不能为了通过求和函数

相反,你可以那样做

 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


文章来源: mysql aggregate functions in query with two joins gives unexpected results