Mysql 在没有 SUBQUERY 的同一查询中使用 GROUP BY 和 SUM(没有 group by)。可能的?
Posted
技术标签:
【中文标题】Mysql 在没有 SUBQUERY 的同一查询中使用 GROUP BY 和 SUM(没有 group by)。可能的?【英文标题】:Mysql using GROUP BY and SUM(without group by) in the same query without SUBQUERY. Possible? 【发布时间】:2021-03-17 09:42:27 【问题描述】:我有一个由两个具有一对多关系的表组成的查询。
产品表
product_id |
---|
1234 |
Products_destinations 表
product_id | destinations_id |
---|---|
1234 | 1 |
1234 | 2 |
我做了一个查询来选择产品ID和所有相关的目的地,这很容易。
SELECT
p.product_id, GROUP_CONCAT(DISTINCT pd.destinations_id) as destinations_list
FROM
`products` p
INNER JOIN products_destinations pd ON pd.product_id = p.product_id
GROUP BY p.product_id
结果是:
product_id | destinations_list |
---|---|
1234 | 1,2 |
现在一个新表进入查询,它也可以与产品具有一对多关系。但是它与 products_destinations dable 没有任何关系。
Products_prices 表
product_id | price |
---|---|
1234 | 200 |
更新后的查询如下所示:
SELECT
p.product_id, GROUP_CONCAT(DISTINCT pd.destinations_id) as destinations_list, SUM(pc.price) as all_prices
FROM
`products` p
INNER JOIN products_destinations pd ON pd.product_id = p.product_id
INNER JOIN products_prices pc ON pc.product_id = p.product_id
GROUP BY p.product_id
现在最终结果如下所示:
product_id | destinations_list | all_prices |
---|---|---|
1234 | 1,2 | 400 |
如您所见,价格显示为 400 而不是 200,因为产品包含两个目的地。在这种类型的查询中是否可以计算产品价格的总和?一种解决方案是使用 SUBQUERY 来计算价格的总和,但这只是一个示例,实际表非常大且充满数据......子查询会大大增加查询时间。
更新:
"count the SUM" 英语不好,抱歉。此查询的问题在于,当产品有多个目的地时,产品价格不正确。例如,产品可以与多个目的地相关并且可以有多个价格。在同一个查询中,我需要选择目的地列表和产品的总价。在此示例中,产品的价格为 200,但因为我还需要检索目的地列表,所以我必须按产品分组,这会导致每个目的地的价格也增加。如果产品价格更高,结果会更糟。最后的结果应该是这样的:
product_id | destinations_list | all_prices |
---|---|---|
1234 | 1,2 | 200 |
而不是这个:
product_id | destinations_list | all_prices |
---|---|---|
1234 | 1,2 | 400 |
【问题讨论】:
“计算总和”是什么意思?您是否想在此示例中获取值2
,因为这是基于 JOINed 数据集中的 两个 记录?那么隐含地,您已经拥有该信息……您可以“计算”destinations_list
包含多少个值,如果您之后在进一步处理这些查询结果时需要此信息。可能还可以添加COUNT()
以直接获取该值。
你看过窗口函数吗?您可以按 product_id 进行分区,并找到一些从给定分区中仅添加第一行的 hacky 方法,例如。 SUM(CASE WHEN partition_row=1 THEN pc.price ELSE 0 END)
.
我自己对“计算总和” 感到很困惑,但我觉得你真正想要的结果只是 200 的实际价格。如果那是在这种情况下,您可以将SUM
替换为MIN
或MAX
(没关系,因为价格仅取决于产品,而不取决于目的地,因此在分组的所有行中都是相同的) .如果您指定 all_prices
真正应该包含的内容,将会很有帮助。
您的Product_prices
表是否有一个主键列来区分同一产品的不同价格?或者可能是product_id, price
上的复合键,这意味着特定产品没有重复价格?如果是,那么SUM(pc.price)/COUNT(DISTINCT pc.price)
应该可以工作。 (或者如果你有 PK 列并允许重复价格)
我更新了我的问题。 product_prices 和 product_destinations 表没有主键,这是一个真正的问题,但目前无法更改。
【参考方案1】:
您遇到的基本问题是价格和目的地之间存在笛卡尔爆炸。任何时候,只要您加入多个与产品具有 1:M 关系的表,您就会开始多行;两个价格和两个目的地将变为 4 行,三个价格和 4 个目的地将变为 12 行
最简单的解决方案是确保只加入 1:1 的行:
SELECT
p.product_id, pd.destinations_list, pc.sumprices
FROM
products p
INNER JOIN (
SELECT product_id, GROUP_CONCAT(destinations_id) as destinations_list
FROM products_destinations
GROUP BY product_id
) pd ON pd.product_id = p.product_id
INNER JOIN (
SELECT product_id, SUM(price) as sumprices
FROM products_prices
GROUP BY product_id
) pc ON pc.product_id = p.product_id
你可以只做这些子查询之一,但我认为没有什么意义,因为你只需要对外部进行分组并在那里处理重复。在每个表的基础上处理聚合更容易,这样最终你就可以在外部以 1:1 的方式加入所有内容。一旦你有了这个,运行一个解释计划并计算出你可以如何明智地索引来改进事情(你还没有发布任何 where 子句)。我很欣赏您说“没有子查询”,但是您正在引起一个问题(笛卡尔爆炸),然后您必须找到解决方法,而且只能到此为止;最好不要一开始就引起问题,而不是在引起问题后找到解决问题的方法。不要太担心查询的显示方式;无论如何,优化器很可能会对其进行大量重写,因此了解它的实际执行方式并为此调整设置会更有效率
【讨论】:
从来没有听说过“笛卡尔爆炸”这个词,这是很棒的新信息!会调查你的解决方案。谢谢!【参考方案2】:另一种方法是关联子查询:
SELECT p.product_id,
(SELECT GROUP_CONCAT(DISTINCT pd.destinations_id)
FROM products_destinations pd
WHERE pd.product_id = p.product_id
) as destinations_list,
(SELECT SUM(pc.price)
FROM products_prices pc
WHERE pc.product_id = p.product_id
) as all_prices
FROM products p ;
注意:这会保留所有产品,即使是那些可能缺少价格或目的地的产品。
此版本的优势在于,如果您使用WHERE
子句过滤产品数量,那么它应该具有非常好的性能——假设product_id
在两个联结表中被索引。
【讨论】:
这看起来很有趣.. 试试看,谢谢!以上是关于Mysql 在没有 SUBQUERY 的同一查询中使用 GROUP BY 和 SUM(没有 group by)。可能的?的主要内容,如果未能解决你的问题,请参考以下文章
MySQL Left Join Subquery with *
MySQL 报错: Subquery returns more than 1 row