给出以下(非常简化的)mysql表结构:
产品
product_categories
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
现在我的问题是:SUM(pt.some_other_numeric_value)
会返回意外值。
我意识到如果有问题的产品与 product_categories 表格有多个关系,那么与 product_tags 表格的每个关系都会被计算为与与 product_categories 表的关系!
例如:如果id = 1的产品与具有ids = 2,3和4的product_categories有关系,并且与product_tags与ids 5和6的关系 - 那么如果我插入GROUP_CONCAT(pt.id)
,那么它确实给出 5,6,5,6,5,6 而不是预期的 5,6 。
起初我怀疑这是连接类型(左连接,右连接,内连接等)的问题,所以我尝试了我所知道的每种连接类型,但无济于事。我还尝试在GROUP BY
子句中包含更多id-fields,但这也没有解决问题。
有人可以向我解释这里究竟出了什么问题吗?
答案 0 :(得分:5)
您通过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()
- 以防你将内部联接设置为LEFT
外连接。
答案 1 :(得分: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