总结多个GROUP BY

Mr.*_*ama 17 sql oracle group-by rollup grouping-sets

假设我有一个表格,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
Run Code Online (Sandbox Code Playgroud)

我正在国家/省级建立一份报告,它给了我以下内容:

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
Run Code Online (Sandbox Code Playgroud)

我希望报告中包含"总体摘要"行,以便最终结果如下所示:

COUNTRY     PROVINCE    SUM(POPULATION)
=======================================
USA         California   5555
USA         Texas        3333
Canada      Ontario      3333
Canada      Manitoba     4444
TOTAL                   16665
Run Code Online (Sandbox Code Playgroud)

我很熟悉ROLLUPs,但我似乎无法找到能让我满意的组合.使用GROUP BY ROLLUP(country, province)包括我想要的总值,但它还包括我不关心的大量额外值.这也是如此GROUP BY ROLLUP(country), province

如何制作"全部"记录?
我正在用a计算它,UNION ALL并用不同的方法重复第一个查询的90%GROUP BY,但因为第一个查询非常重要,结果是缓慢而丑陋的代码.

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

ype*_*eᵀᴹ 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
   );
Run Code Online (Sandbox Code Playgroud)

请参阅SQL-Fiddle


Mr.*_*ama 6

好吧,我终于提出了两种灵活的方法,并没有让我感觉像一个可怕的程序员.


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

如果我将查询分成两部分并使用a 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 ();
Run Code Online (Sandbox Code Playgroud)

查询有效,但不能很好地扩展.您需要进行的计算越多,重复自己的时间就越多.

通过使用a GROUPING SETS,我可以指定我希望以两种不同的方式分组数据:

SELECT country, province, SUM(population)
FROM census
GROUP BY GROUPING SETS( (country, province), () );
Run Code Online (Sandbox Code Playgroud)

现在我们到了某个地方!但是我们的结果排怎么样?我们如何检测它并相应地标记它?这就是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), () );
Run Code Online (Sandbox Code Playgroud)

如果我们不喜欢这种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) );
Run Code Online (Sandbox Code Playgroud)

随意为自己尝试两种方法!
http://sqlfiddle.com/#!4/12ad9/102