假设我有一个名为census
的表,其中包含以下信息:
COUNTRY PROVINCE CITY POPULATION
==============================================
USA California Sacramento 1234
USA California SanFran 4321
USA Texas Houston 1111
USA Texas Dallas 2222
Canada Ontario Ottawa 3333
Canada Manitoba Winnipeg 4444
我在国家/省级建立报告,它给了我以下内容:
SELECT country, province, SUM(population)
FROM census
GROUP BY country, province;
COUNTRY PROVINCE SUM(POPULATION)
=======================================
USA California 5555
USA Texas 3333
Canada Ontario 3333
Canada Manitoba 4444
我希望得到一个"整体摘要"行包含在报告中,以便最终结果如下:
COUNTRY PROVINCE SUM(POPULATION)
=======================================
USA California 5555
USA Texas 3333
Canada Ontario 3333
Canada Manitoba 4444
TOTAL 16665
我熟悉ROLLUP
,但我似乎无法找到一个能让我了解我想要的东西的组合。使用GROUP BY ROLLUP(country, province)
包含我想要的总价值,但它还包含大量额外值,我并不关心。 GROUP BY ROLLUP(country), province
我怎样才能完成"总计"记录?
我目前正在使用UNION ALL
进行计算,并使用不同的GROUP BY
重复第一个查询的90%,但由于第一个查询非常重要,因此结果是缓慢且难看的代码。
对于那些想要玩这个游戏的人来说,这是一个SQL小提琴:http://sqlfiddle.com/#!4/12ad9/5
答案 0 :(得分:13)
这正是 GROUPING SETS
表达式的目的:
SELECT country, province, SUM(population)
FROM census
GROUP BY GROUPING SETS
( (country, province), -- first group by country and province
() -- then by (nothing), i.e. a total grouping
);
请参阅SQL-Fiddle
答案 1 :(得分:6)
好的,我终于提出了两种灵活的方法,不会让我觉得自己像个可怕的程序员。
第一个解决方案涉及GROUPING SETS
我实际上要做的是将表达式分为两个不同的级别:一个在整体级别,一个在(country, province)
级别。
如果我将查询分成两部分并使用UNION ALL
,则一半会有GROUP BY country, province
而另一半会缺少分组子句。如果我们愿意,未分组的部分也可以表示为GROUP BY ()
。这将在一瞬间派上用场。
这给了我们类似的东西:
SELECT country, province, SUM(population)
FROM census
GROUP BY country, province
UNION ALL
SELECT NULL AS country, NULL AS province, SUM(population)
FROM census
GROUP BY ();
查询有效,但它不能很好地扩展。您需要进行的计算越多,重复自己的时间就越多。
通过使用GROUPING SETS
,我可以指定我希望以两种不同的方式对数据进行分组:
SELECT country, province, SUM(population)
FROM census
GROUP BY GROUPING SETS( (country, province), () );
现在我们到了某个地方!但是我们的结果排怎么样?我们如何检测它并相应地标记它?那是GROUPING
函数的来源。如果由于GROUP BY语句而导致列为NULL,则返回1。
SELECT
CASE
WHEN GROUPING(country) = 1 THEN 'TOTAL'
ELSE country
END AS country,
province,
SUM(population),
GROUPING(country) AS grouping_flg
FROM census
GROUP BY GROUPING SETS ( (country, province), () );
如果我们不喜欢GROUPING SETS
方法,我们仍然可以使用传统的ROLLUP
,但稍作修改。
我们不是将每个列单独传递给ROLLUP
,而是将列集合作为一个集合传递,将它们包含在括号中。这样就可以将列集视为单个组,而不是多个组。以下查询将为您提供与上一个相同的结果:
SELECT
CASE
WHEN GROUPING(country) = 1 THEN 'TOTAL'
ELSE country
END AS country,
province,
SUM(population),
GROUPING(country) AS grouping_flg
FROM census
GROUP BY ROLLUP( (country, province) );
随意为自己尝试两种方法!
http://sqlfiddle.com/#!4/12ad9/102
答案 2 :(得分:3)
在Oracle中,您可以使用having
子句执行此操作:
SELECT coalesce(c.country, 'Total') as province, c.country, SUM(c.population)
FROM census c
GROUP BY ROLLUP(c.country, c.province)
HAVING c.province is not null or
c.province is null and c.country is null;
Here是SQL小提琴。
答案 3 :(得分:2)
首先想到的是在应用rollup
后过滤掉小计:
SELECT *
FROM (SELECT country, province, SUM (population)
FROM census
GROUP BY ROLLUP (country, province))
WHERE province IS NOT NULL OR country IS NULL;
通过在GROUPING_ID
子句中使用HAVING
,您可以更紧凑地完成同样的事情:
SELECT country,
province,
SUM (population)
FROM census
GROUP BY ROLLUP (country, province)
HAVING GROUPING_ID (country, province) <> 1
而且,正如@Anssssss指出的那样,您还可以使用WHERE
子句中第一个答案中HAVING
子句的条件:
SELECT country, province, SUM (population)
FROM census
GROUP BY ROLLUP (country, province)
HAVING province IS NOT NULL OR country IS NULL
答案 4 :(得分:-1)
你可以使用联盟:
SELECT country, province, SUM(population)
FROM census
GROUP BY country, province
UNION
SELECT
'Total', '', SUM(population)
FROM census
答案 5 :(得分:-1)
我想出了一个使用Union的SQL来将Total添加到结果的末尾。您可以看到query here
SELECT country, province, SUM(population) as population, 0 as OrderBy
FROM census
GROUP BY country, province
UNION
SELECT country, province, population, 1 as OrderBy FROM (
SELECT 'Total' as country, '' as province, SUM(population) as population
FROM census
)
ORDER BY OrderBy;