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 替换为MINMAX(没关系,因为价格仅取决于产品,而不取决于目的地,因此在分组的所有行中都是相同的) .如果您指定 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子查询(SubQuery)

MySQL Subquery Summary

MySQL Left Join Subquery with *

MySQL 报错: Subquery returns more than 1 row

MySQL 报错: Subquery returns more than 1 row

MySQL--5--subquery和连接