MySQL 8.0 GROUP BY / FULL_GROUP_BY

时间:2019-02-07 21:12:14

标签: mysql sql group-by

自从升级到MySQL 8以来,我有很多查询不符合MySQL的新的完全分组方式设置。下面是其中一个查询的简化版本。我有很多,在阅读所有代码之前,我想完全理解问题。

我在数据库中有以下记录:

[prod_id] => 1
[prod_name] => Product 1
[prod_new] => 50.00
[prod_size] => L

[prod_id] => 2
[prod_name] => Product 1
[prod_new] => 45.00
[prod_size] => M

[prod_id] => 3
[prod_name] => Product 1
[prod_new] => 40.00
[prod_size] => S

[prod_id] => 4
[prod_name] => Product 4
[prod_new] => 100.00
[prod_size] => M

[prod_id] => 5
[prod_name] => Product 5
[prod_new] => 200.00
[prod_size] => M

当我在MySQL 5.x中运行以下查询时,我得到了3个结果。包含产品1、4、5。以及相应的名称,价格和尺寸。

SELECT prod_id, prod_name, prod_price, prod_size 
FROM prod_product
GROUP BY prod_name

自升级以来,我得到了有关非聚合列的广为人知的错误。所以我想解决这个问题,但是在某些情况下,这会给我带来不良的结果。可以说,出于某种原因,我想要最高的产品ID。

SELECT MAX(prod_id), prod_name, ANY_VALUE(prod_price), ANY_VALUE(prod_size)
FROM prod_product
GROUP BY prod_name

这将给我产品ID为3、4、5。但是,如果使用产品ID 3,它将为我提供产品ID 1的价格和大小。

显然,这是不想要的行为。我假设,由于prod_id是主键,因此数据库知道要显示具有相应ID的值。当我说MAX(prod_id)时,已经精确地确定了该组中的一条记录,为什么要给我该组中其他记录的值?

我想我在这里错过了一些重要的事情。 =)

谢谢!

2 个答案:

答案 0 :(得分:1)

  

我会假定,由于prod_id是主键,因此数据库知道要显示具有相应ID的值。

为什么知道呢?

请考虑以下查询:

SELECT prod_name, prod_id, MIN(prod_price), MAX(prod_price)
FROM prod_product
GROUP BY prod_name

此处prod_id应该返回哪个值?对应最低价格的产品?还是对应最高价格的产品?

此外,如果有多个产品以最低或最高价格捆绑在一起,该怎么办?它应该返回哪一个?

SELECT prod_name, prod_id, AVG(prod_price)
FROM prod_product
GROUP BY prod_name

现在应该推断哪个prod_id?汇总计算AVG()可能返回的值与任何单个产品都不对应。

汇总SUM()也会发生同样的情况。

事实是,聚合函数和组中的特定行之间没有隐式关联。使用非聚合表达式时,您不应该期望SQL会猜测要引用的组中的哪一行。

答案 1 :(得分:0)

如果您希望每组行中的第一条记录具有由prod_name排序的相同prod_id,则可以使用 window函数 ROW_NUMBER(),即在MySQL 8中可用:

SELECT x.prod_id, x.prod_name, x.prod_new, x.prod_size
FROM (
    SELECT 
        p.prod_id, p.prod_name, p.prod_new, p.prod_size, 
        ROW_NUMBER() OVER(PARTITION BY p.prod_name ORDER BY p.prod_id) rn
    FROM prod_product p
) x WHERE x.rn = 1

内部查询为每个组中的每个记录分配一个数字,外部查询在每个组中的第一个记录中过滤。

Demo on DB Fiddle

WITH prod_product AS (
    SELECT 1 prod_id, 'Product 1' prod_name, 50 prod_new, 'L' prod_size
    UNION ALL SELECT 2, 'Product 1', 45, 'M'
    UNION ALL SELECT 3, 'Product 1', 40, 'S'
    UNION ALL SELECT 4, 'Product 4', 100, 'M'
    UNION ALL SELECT 5, 'Product 5', 200, 'M' 
)
SELECT x.prod_id, x.prod_name, x.prod_new, x.prod_size
FROM (
    SELECT 
        p.prod_id, p.prod_name, p.prod_new, p.prod_size, 
        ROW_NUMBER() OVER(PARTITION BY p.prod_name ORDER BY p.prod_id) rn
    FROM prod_product p
) x WHERE x.rn = 1;
| prod_id | prod_name | prod_new | prod_size |
| ------- | --------- | -------- | --------- |
| 1       | Product 1 | 50       | L         |
| 4       | Product 4 | 100      | M         |
| 5       | Product 5 | 200      | M         |