Maj*_*jiy 1 mysql join aggregate-functions
给出以下(非常简化的)mysql表结构:
制品
产品类别
product_tags
我试图找到与某个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
Run Code Online (Sandbox Code Playgroud)
现在我的问题是: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-fields ,但这也没有解决问题.
有人可以向我解释这里究竟出了什么问题吗?
您通过关系将"main"(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 ;
Run Code Online (Sandbox Code Playgroud)
避免这个问题的另一种方法是 - 在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 ;
Run Code Online (Sandbox Code Playgroud)
在COALESCE()那里不是严格需要的 - 以防万一你将内部连接与LEFT外部连接组合在一起.
| 归档时间: |
|
| 查看次数: |
1835 次 |
| 最近记录: |