18

假设我有一个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

我对ROLLUPs 很熟悉,但我似乎无法找到一个可以让我得到我想要的东西的组合。UsingGROUP BY ROLLUP(country, province)包括我想要的总价值,但它也包括大量我不关心的额外价值。这也适用于GROUP BY ROLLUP(country), province

我怎样才能制作“总”记录?
我目前正在用 a 计算它,UNION ALL并用 a different 重复 90% 的第一个查询GROUP BY,但是因为第一个查询很重要,所以结果是缓慢而丑陋的代码。

对于那些想要玩这个的人来说,这是一个 SQL Fiddle:http ://sqlfiddle.com/#!4/12ad9/5

4

6 回答 6

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

于 2014-09-09T21:33:01.330 回答
6

好的,我终于想出了两种灵活的方法,不会让我觉得自己是个糟糕的程序员。


第一个解决方案涉及GROUPING SETS.
我实际上要做的是将表达式分为两个不同的级别:一个在整体级别,一个在(country, province)级别。

如果我将查询分成两部分并使用 a UNION ALL,那么一半将有 a 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

于 2014-09-09T22:14:52.340 回答
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;

是 SQL 小提琴。

于 2014-09-09T21:28:14.900 回答
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
于 2014-09-09T21:01:13.537 回答
-1

我想出了一个使用 Union 将 Total 添加到结果末尾的 sql。您可以在此处查看查询

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;
于 2014-09-09T21:18:55.713 回答
-1

你可以使用联合:

SELECT country, province, SUM(population)
FROM census
GROUP BY country, province
UNION
SELECT
   'Total', '', SUM(population)
FROM census
于 2014-09-09T20:58:33.227 回答