使用 MySQL 通过 JOIN 获取 GROUP BY 中的 SUM

Posted

技术标签:

【中文标题】使用 MySQL 通过 JOIN 获取 GROUP BY 中的 SUM【英文标题】:Get SUM in GROUP BY with JOIN using MySQL 【发布时间】:2011-03-20 06:00:09 【问题描述】:

我在 mysql 5.1.38 中有两个表。

products
+----+------------+-------+------------+
| id | name       | price | department |
+----+------------+-------+------------+
|  1 | Fire Truck | 15.00 | Toys       |
|  2 | Bike       | 75.00 | Toys       |
|  3 | T-Shirt    | 18.00 | Clothes    |
|  4 | Skirt      | 18.00 | Clothes    |
|  5 | Pants      | 22.00 | Clothes    |
+----+------------+-------+------------+

ratings
+------------+--------+
| product_id | rating |
+------------+--------+
|          1 |      5 |
|          2 |      5 |
|          2 |      3 |
|          2 |      5 |
|          3 |      5 |
|          4 |      5 |
|          5 |      4 |
+------------+--------+

我的目标是获得在每个部门中获得 5 星评级的所有产品的总价格。像这样的。

+------------+-------------+
| department | total_price |
+------------+-------------+
| Clothes    | 36.00       |  /* T-Shirt and Skirt */
| Toys       | 90.00       |  /* Fire Truck and Bike */
+------------+-------------+

如果可以的话,我想在没有子查询的情况下这样做。起初我尝试使用 sum() 进行连接。

select department, sum(price) from products
join ratings on product_id=products.id
where rating=5 group by department;
+------------+------------+
| department | sum(price) |
+------------+------------+
| Clothes    |      36.00 |
| Toys       |     165.00 |
+------------+------------+

如您所见,玩具部门的价格不正确,因为 Bike 有两个 5 星评级,因此由于加入,该价格被计算了两次。

然后我尝试在总和中添加 distinct。

select department, sum(distinct price) from products
join ratings on product_id=products.id where rating=5
group by department;
+------------+---------------------+
| department | sum(distinct price) |
+------------+---------------------+
| Clothes    |               18.00 |
| Toys       |               90.00 |
+------------+---------------------+

但是后来服装部门关闭了,因为两种产品价格相同。

目前我的解决方法是获取产品的独特性(id)并使用它来使价格独一无二。

select department, sum(distinct price + id * 100000) - sum(id * 100000) as total_price
from products join ratings on product_id=products.id
where rating=5 group by department;
+------------+-------------+
| department | total_price |
+------------+-------------+
| Clothes    |       36.00 |
| Toys       |       90.00 |
+------------+-------------+

但这感觉就像一个愚蠢的黑客。没有子查询有没有更好的方法来做到这一点?谢谢!

【问题讨论】:

你对子查询有什么看法? 我的联接和条件更加复杂和动态,我的 ORM(Active Record)不能很好地支持子查询。 你如何从你的第二张表中知道评级属于哪个部门? @Charles,你是说收视率表吗?它根据所属的产品(product_id)知道部门。 使用 Active Record 有什么限制?你可以使用两个查询吗?可以使用内联表定义吗? 【参考方案1】:

用途:

  SELECT p.department,
         SUM(p.price) AS total_price
    FROM PRODUCTS p
    JOIN (SELECT DISTINCT 
                 r.product_id,
                 r.rating
            FROM RATINGS r) x ON x.product_id = p.id
                             AND x.rating = 5
GROUP BY p.department

从技术上讲,这不使用子查询 - 它使用派生表/内联视图。

【讨论】:

感谢 OMG 小马!这完美解决了我今天遇到的一个问题。我的具体情况需要在派生表上进行左连接,并将 SUM 放在派生表定义中,但它的效果很好。 EXPLAIN 结果看起来也不算太糟糕,所以我们将看看它是如何扩展的。【参考方案2】:

您无法找到解决方案的主要原因是所提供的架构存在根本缺陷。您不应该允许一个表有两行彼此完全重复。 每个表都应该有一种方法来唯一标识每一行,即使它是所有列的组合。现在,如果我们更改ratings 表,使其有一个名为IdAUTO_INCREMENT 列,问题就更简单了:

Select products.department, Sum(price) As total_price
From products
    Left Join ratings As R1
        On R1.product_id = products.id
            And R1.rating = 5
    Left Join ratings As R2
        On R2.product_id = R1.product_id
            And R2.rating = R1.rating
            And R2.Id > R1.Id
Where R2.Id Is Null
Group By products.department

【讨论】:

实际上我在实际应用程序中确实有一个自动递增的 id 字段,这要复杂得多。我试图在这里尽可能地简化所有内容,但似乎我通过去掉 rating.id 走得太远了。感谢您发布此选项!【参考方案3】:

如果查询中没有子查询somewhere,我想不出任何方法。您也许可以使用 View 来掩盖子查询的使用。

除此之外,您最好的选择可能是找到进行计算所需的最小数据集并在前端进行计算。这是否可能取决于您的特定数据 - 行数等。

另一种选择(实际上,也许这是最好的选择......)是获得一个新的 ORM 或完全不使用它;)

这个视图可以让你绕过子查询:

CREATE VIEW Distinct_Product_Ratings
AS
    SELECT DISTINCT
        product_id,
        rating
    FROM
        Ratings

【讨论】:

【参考方案4】:

您可以执行两个查询。第一个查询:

SELECT DISTINCT product_id FROM rating WHERE rating = 5;

然后,获取每个 ID 并手动将它们放入第二个查询中:

选择部门,总和(价格)作为总价格 来自产品 WHERE product_id 在 (1,2,3,4) 按部门分组;

这是无法使用子查询的解决方法。没有它们,就无法消除join造成的重复记录。

【讨论】:

以上是关于使用 MySQL 通过 JOIN 获取 GROUP BY 中的 SUM的主要内容,如果未能解决你的问题,请参考以下文章

MySQL Multiple INNER JOIN + GROUP BY 未按预期工作

如何通过 join 和 group by 在 C# Linq 中获取 Min?

MySQL 使用 JOIN + GROUP + 外键构造查询

MySQL 使用 JOIN 和 GROUP_CONCAT

带有 LEFT JOIN 和 GROUP BY 的 COUNT(*) 在 MySQL 中包含 NULL

MySQL:使用 JOIN 和 GROUP_CONCAT 进行更新