鉴于以下(非常简单的)mysql的表结构:
制品
产品类别
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要么解决问题。
谁能给我解释一下什么是真正去错在这里?
你加入一个“主”( product
)表两个表( tags
和categories
通过) 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
外连接。
你不能为了通过求和函数
相反,你可以那样做
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