问题描述
我正在尝试使用 GROUP ... WITH ROLLUP 来获取多个分组值的一系列总计,但没有小计。因此,在我人为的示例中,我有一个表 fruits
,如下所示:
SELECT * FROM fruits ORDER BY fruit;
+----+----------+------+-------+
| id | fruit | size | price |
+----+----------+------+-------+
| 1 | apple | 3 | 4.99 |
| 2 | apple | 4 | 5.99 |
| 3 | apple | 6 | 2.99 |
| 8 | apple | 3 | 3.00 |
| 9 | apple | 4 | 6.50 |
| 4 | banana | 2 | 3.50 |
| 5 | banana | 3 | 2.10 |
| 6 | banana | 1 | 8.99 |
| 7 | banana | 7 | 6.99 |
| 10 | banana | 3 | 3.50 |
| 14 | banana | 7 | 8.00 |
| 11 | cucumber | 1 | 1.50 |
| 12 | cucumber | 1 | 2.30 |
| 13 | cucumber | 2 | 3.30 |
+----+----------+------+-------+
假设我想按 fruit
和 size
对水果表进行分组,并在底部显示平均价格。我可以使用 GROUP WITH ROLLUP 来实现这一点:
SELECT fruit,size,AVG(price) FROM fruits GROUP BY fruit,size WITH ROLLUP;
+----------+------+------------+
| fruit | size | AVG(price) |
+----------+------+------------+
| apple | 3 | 3.995000 |
| apple | 4 | 6.245000 |
| apple | 6 | 2.990000 |
| apple | NULL | 4.694000 |
| banana | 1 | 8.990000 |
| banana | 2 | 3.500000 |
| banana | 3 | 2.800000 |
| banana | 7 | 7.495000 |
| banana | NULL | 5.513333 |
| cucumber | 1 | 1.900000 |
| cucumber | 2 | 3.300000 |
| cucumber | NULL | 2.366667 |
| NULL | NULL | 4.546429 |
+----------+------+------------+
但我不想要所有这些小计行...我只想要最后一行,其中 fruit
和 size
都显示为 NULL。换句话说,这是:
+----------+------+------------+
| fruit | size | AVG(price) |
+----------+------+------------+
| apple | 3 | 3.995000 |
| apple | 4 | 6.245000 |
| apple | 6 | 2.990000 |
| banana | 1 | 8.990000 |
| banana | 2 | 3.500000 |
| banana | 3 | 2.800000 |
| banana | 7 | 7.495000 |
| cucumber | 1 | 1.900000 |
| cucumber | 2 | 3.300000 |
| NULL | NULL | 4.546429 |
+----------+------+------------+
我无法访问 MysqL 8.0,因此我无法使用 GROUPING()
函数 described in the docs 来确定行中的值是表示小计还是总计。所以我想我必须过滤掉 NULL
值。
我试着像这样适应this answer to a similar question:
SELECT fruit,size WITH ROLLUP HAVING size IS NOT NULL or fruit IS NULL;
+----------+------+------------+
| fruit | size | AVG(price) |
+----------+------+------------+
| apple | 3 | 3.995000 |
| apple | 4 | 6.245000 |
| apple | 6 | 2.990000 |
| banana | 1 | 8.990000 |
| banana | 2 | 3.500000 |
| banana | 3 | 2.800000 |
| banana | 7 | 7.495000 |
| cucumber | 1 | 1.900000 |
| cucumber | 2 | 3.300000 |
+----------+------+------------+
不过,正如您所看到的,这不会产生我想要的结果 - 它只是完全砍掉了总计。我知道我只想获取大小为非空且水果为空或不为空的行,但是我尝试测试 NULL 的各种方法没有按预期工作:
SELECT fruit,AVG(price),fruit IS NULL,fruit IS NOT NULL,ISNULL(fruit),fruit = "" FROM fruits GROUP BY fruit,size WITH ROLLUP;
+----------+------+------------+---------------+-------------------+---------------+------------+
| fruit | size | AVG(price) | fruit IS NULL | fruit IS NOT NULL | ISNULL(fruit) | fruit = "" |
+----------+------+------------+---------------+-------------------+---------------+------------+
| apple | 3 | 3.995000 | 0 | 1 | 0 | 0 |
| apple | 4 | 6.245000 | 0 | 1 | 0 | 0 |
| apple | 6 | 2.990000 | 0 | 1 | 0 | 0 |
| apple | NULL | 4.694000 | 0 | 1 | 0 | 0 |
| banana | 1 | 8.990000 | 0 | 1 | 0 | 0 |
| banana | 2 | 3.500000 | 0 | 1 | 0 | 0 |
| banana | 3 | 2.800000 | 0 | 1 | 0 | 0 |
| banana | 7 | 7.495000 | 0 | 1 | 0 | 0 |
| banana | NULL | 5.513333 | 0 | 1 | 0 | 0 |
| cucumber | 1 | 1.900000 | 0 | 1 | 0 | 0 |
| cucumber | 2 | 3.300000 | 0 | 1 | 0 | 0 |
| cucumber | NULL | 2.366667 | 0 | 1 | 0 | 0 |
| NULL | NULL | 4.546429 | 0 | 0 | 0 | NULL |
+----------+------+------------+---------------+-------------------+---------------+------------+
这些结果都没有任何意义。我会假设 fruit is NULL
或 ISNULL(fruit)
会在水果的值实际上为 NULL 时返回 1。但他们没有。所以我想知道这是否与空字符串和 null 之间的区别有关……但我不明白为什么,当我测试 fruit = ""
时,当水果为 NULL 时它返回 NULL。>
我应该使用什么条件来测试 NULL,以便我可以从我的表中排除我想要的结果?
解决方法
将您的聚合查询用作子查询和过滤器:
SELECT *
FROM (
SELECT fruit,size,AVG(price) avg_price
FROM fruits
GROUP BY fruit,size
WITH ROLLUP
) t
WHERE (fruit IS NOT NULL AND size IS NOT NULL)
OR (fruit IS NULL AND size IS NULL)
你也可以不用子查询,使用 HAVING 子句:
SELECT fruit,AVG(price) avg_price
FROM fruits
GROUP BY fruit,size
WITH ROLLUP
HAVING (fruit IS NOT NULL AND size IS NOT NULL)
OR (fruit IS NULL AND size IS NULL)
参见demo。
,不需要子查询。您可以使用 having
子句:
select fruit,avg(price)
from fruits
group by fruit,size with rollup
having fruit is not null and size is not null or
fruit is null and size is null;
还有其他表达having
的方式,例如:
having (fruit is not null) = (size is not null)
Here 是一个 dbfiddle。