ORACLE_Quiz_test GROUPING SETS测试题 答案和自己的理解

GROUPING SETS

第一题:

题目链接🔗GROUPING SETS

首先看题:

I have a table of customers:

create table qz_customers (
   cust_id    integer primary key
 , segment    varchar2(10)
 , category   varchar2(10)
 , country    varchar2(10)
)
/

insert into qz_customers values (1, 'Medium', 'Commerce', 'Germany')
/
insert into qz_customers values (2, 'Medium', 'Private' , 'Denmark')
/
insert into qz_customers values (3, 'Medium', 'Commerce', 'Germany')
/
insert into qz_customers values (4, 'Large' , 'Commerce', 'Germany')
/
insert into qz_customers values (5, 'Large' , 'Commerce', 'Holland')
/
insert into qz_customers values (6, 'Small' , 'Commerce', 'Germany')
/
insert into qz_customers values (7, 'Small' , 'Private' , 'Denmark')
/
insert into qz_customers values (8, 'Small' , 'Private' , 'Denmark')
/
insert into qz_customers values (9, 'Small' , 'Commerce', 'Holland')
/
commit
/
The table specifies:

which segment (size) the customer belongs to
which category (private or commercial) the customer belongs to
which country the customer resides in
I would like a list of how many customers we have, grouped by:

segment and category
segment
country
grand total
For that purpose I have this incomplete query:

select case grouping(c.segment)
          when 1 then 'Total'
          else c.segment
       end as segment
     , case grouping(c.category)
          when 1 then 'Total'
          else c.category
       end as category
     , case grouping(c.country)
          when 1 then 'Total'
          else c.country
       end as country
     , count(*) as customers
  from qz_customers c
##REPLACE##
 order by grouping(c.segment) , c.segment
        , grouping(c.category), c.category
        , grouping(c.country) , c.country
/
Which of the choices contain code that can replace ##REPLACE## to make the query return this desired output:

SEGMENT    CATEGORY   COUNTRY     CUSTOMERS
---------- ---------- ---------- ----------
Large      Commerce   Total               2
Large      Total      Total               2
Medium     Commerce   Total               2
Medium     Private    Total               1
Medium     Total      Total               3
Small      Commerce   Total               2
Small      Private    Total               2
Small      Total      Total               4
Total      Total      Denmark             3
Total      Total      Germany             4
Total      Total      Holland             2
Total      Total      Total               9

题目要求主要看这句:
I would like a list of how many customers we have, grouped by:
segment and category
segment
country
grand total


选项一

 group by cube(c.segment, c.category, c.country)
having grouping_id(c.segment, c.category, c.country) in (1,3,6,7)

这里是给出的解析:

GROUP BY CUBE creates all 8 sub- and grand-total combinations of the three columns. Then the HAVING clause filters, so we only keep the 4 that we want. GROUPING_ID is a bit-pattern where ‘1’ in the bit pattern is the value of the GROUPING() function for that column. So “001” is where segment and category have values but country is total, or in other words 1 is subtotals at segment/category level. “110” is where segment and category are total but country has values, or in other words 6 is subtotals at country level. And so on.

CUBE就是把GROUP BY字句中聚合键的“所有可能的组合”的汇总结果集中到一个结果中,这里有三个字段所以有8种统计方式,但是根据题目中的要求,只需统计四种;
GROUPING_ID中的1代表GROUPING函数中这一行的值(为合计值时为1);
所以001(即1)表示segment 和 category 有值 而 country 是合计值;110(即6)表示segment 和 category 有合计值 而 country 是值。
看图更加直观:
在这里插入图片描述


选项二

group by rollup(c.segment, c.category, c.country)

解析:

This rollup creates subtotal groupings for segment/category/country, segment/category, segment, and grand total.

直接roullup三个字段会得到 segment/category/country, segment/category, segment, and grand total,不符合要求。
在这里插入图片描述


选项三

group by grouping sets (
             (c.segment, c.category)
           , (c.segment)
           , (c.country)
           , ()
          )

解析:

Using GROUPING SETS we can explicitly specify which combinations we want, rather than using CUBE or ROLLUP. This is the specification for the 4 different combinations we want. The empty set of parentheses means grand total.

最后一句: “()”代表了grand total
加上(c.segment, c.category), (c.segment), (c.country),符合题目要求


选项四

group by grouping sets (
             rollup(c.segment, c.category)
           , (c.country)
           , ()
          )

解析:

ROLLUP can be used within GROUPING SETS. ROLLUP here creates the sets (c.segment, c.category), (c.segment) and (). As we also manually have a (), we get a wrong output with two grand totals


选项五

 group by grouping sets (
             rollup(c.segment, c.category)
           , rollup(c.country)
          )

解析:

The first ROLLUP creates the sets (c.segment, c.category), (c.segment) and (). Then the second ROLLUP creates the sets (c.country) and (). So again we have two grand totals in a wrong output like the previous choice.

四五都是一样的错误,由于rollup()会产生“()”,即grand total,输出会重复两个grand total


选项六

 group by grouping sets (
             rollup(c.segment, c.category)
           , (c.country)
          )

所以六才是对的


选项七

 group by grouping sets (
            (c.segment, c.category)
          , (c.segment)
          , rollup(c.country)
         )

解析:

The ROLLUP creates the sets (c.country) and (). Then we manually specify the other two sets and end up with the same 4 correct sets as choice 3 and the previous choice.



第二题:

题目链接🔗GROUPING SETS

首先看题:

I create and populate the following table:

CREATE TABLE plch_sales
(
   product     VARCHAR2(10)
 , country     VARCHAR2(10)
 , year        NUMBER
 , sales       NUMBER
)
/

BEGIN
   INSERT INTO plch_sales VALUES ('BANANA', 'US', 2009, 200);
   INSERT INTO plch_sales VALUES ('BANANA', 'US', 2010, 300);
   INSERT INTO plch_sales VALUES ('BANANA', 'GB', 2009, 400);
   INSERT INTO plch_sales VALUES ('BANANA', 'GB', 2010, 350);
   INSERT INTO plch_sales VALUES ('BANANA', 'DK', 2010, 250);
   INSERT INTO plch_sales VALUES ('APPLE' , 'US', 2009, 100);
   INSERT INTO plch_sales VALUES ('APPLE' , 'GB', 2009, 150);
   INSERT INTO plch_sales VALUES ('APPLE' , 'GB', 2010, 150);
   INSERT INTO plch_sales VALUES ('APPLE' , 'DK', 2009, 250);
   INSERT INTO plch_sales VALUES ('APPLE' , 'DK', 2010, 250);
   INSERT INTO plch_sales VALUES ('PEAR'  , 'GB', 2010, 150);
   INSERT INTO plch_sales VALUES ('PEAR'  , 'DK', 2009, 300);
   INSERT INTO plch_sales VALUES ('PEAR'  , 'DK', 2010, 350);

   COMMIT;
END;
/
My boss wants sales totals listed by product, by country and a grand total.

Which of the choices produce this desired output:

TOTAL   PRODUCT    COUNTRY         SALES
------- ---------- ---------- ----------
Product APPLE      TOTAL             900
Product BANANA     TOTAL            1500
Product PEAR       TOTAL             800
Country TOTAL      DK               1400
Country TOTAL      GB               1200
Country TOTAL      US                600
Grand   TOTAL      TOTAL            3200

重点是这句:

My boss wants sales totals listed by product, by country and a grand total.

即boss要求根据 product、country、总计 进行分组合计。


Choice 1 ×

SELECT 'Product' total
     , CASE GROUPING(s.product)
         WHEN 1 THEN 'TOTAL'
         ELSE s.product
       END product
     , 'TOTAL' country
     , SUM(s.sales) sales
FROM plch_sales s
GROUP BY s.product
UNION ALL
SELECT 'Country' total
     , 'TOTAL' product
     , CASE GROUPING(s.country)
         WHEN 1 THEN 'TOTAL'
         ELSE s.country
       END country
     , SUM(s.sales) sales
FROM plch_sales s
GROUP BY s.country
UNION ALL
SELECT 'Grand' total
     , 'TOTAL' product
     , 'TOTAL' country
     , SUM(s.sales) sales
FROM plch_sales s
ORDER BY product, country;
  • This creates all the required sub- and grandtotals as required in a do-it-yourself implementation of GROUPING SETS.
  • But the ORDER BY orders the result by the column ALIASES product and country - therefore the grand total comes before the US country subtotal. Beware ordering by column aliases with the same name as the columns.
  • aliases:n. 别名(alias的复数)
  • ORDER BY product, country 这里的排序是union后的表格进行统一排序,所以会将将’total’和‘US’比较,'total’排名反而靠前,不符合题意。

Choice 2 √

SELECT * FROM (
   SELECT 'Product' total
        , CASE GROUPING(s.product)
            WHEN 1 THEN 'TOTAL'
            ELSE s.product
          END product
        , 'TOTAL' country
        , SUM(s.sales) sales
   FROM plch_sales s
   GROUP BY s.product
   ORDER BY s.product
)
UNION ALL
SELECT * FROM (
   SELECT 'Country' total
        , 'TOTAL' product
        , CASE GROUPING(s.country)
            WHEN 1 THEN 'TOTAL'
            ELSE s.country
          END country
        , SUM(s.sales) sales
   FROM plch_sales s
   GROUP BY s.country
   ORDER BY s.country
)
UNION ALL
SELECT 'Grand' total
     , 'TOTAL' product
     , 'TOTAL' country
     , SUM(s.sales) sales
FROM plch_sales s;
  • This is a do-it-yourself implementation of GROUPING SETS making three sets of sums and doing UNION ALL. Note the ORDER BY has to be in each part to make this work (or each part would have had to select something to use in an outer ORDER BY.)

  • 与上一选项不同的是,这里是将查询结果分开排序后再 UNION ALL

  • Warning: This relies on UNION ALL preserving the row order of the subqueries. This does work (as the optimizer will not do unnecessary sorting after the UNION ALL) but it is undocumented. As such there is a (slight) possibility it might change in future releases.

  • 这里的结果顺序取决于UNION ALL 之前的排序

An alternative(可替代的) answer would be this:

SELECT s2.total
     , s2.product
     , s2.country
     , s2.sales
FROM (
   SELECT 1 subselect
        , GROUPING(s.product) subtotal
        , 'Product' total
        , CASE GROUPING(s.product)
            WHEN 1 THEN 'TOTAL'
            ELSE s.product
          END product
        , 'TOTAL' country
        , SUM(s.sales) sales
   FROM plch_sales s
   GROUP BY s.product
   UNION ALL
   SELECT 2 subselect
        , GROUPING(s.country) subtotal
        , 'Country' total
        , 'TOTAL' product
        , CASE GROUPING(s.country)
            WHEN 1 THEN 'TOTAL'
            ELSE s.country
          END country
        , SUM(s.sales) sales
   FROM plch_sales s
   GROUP BY s.country
   UNION ALL
   SELECT 3 subselect
        , 1 subtotal
        , 'Grand' total
        , 'TOTAL' product
        , 'TOTAL' country
        , SUM(s.sales) sales
   FROM plch_sales s
) s2
ORDER BY s2.subselect
       , s2.subtotal
       , s2.product
       , s2.country;

Choice 3 ×

SELECT CASE GROUPING_ID(s.product, s.country)
         WHEN 1 THEN 'Product'
         WHEN 2 THEN 'Country'
         WHEN 3 THEN 'Grand'
       END total
     , CASE GROUPING(s.product)
         WHEN 1 THEN 'TOTAL'
         ELSE s.product
       END product
     , CASE GROUPING(s.country)
         WHEN 1 THEN 'TOTAL'
         ELSE s.country
       END country
     , SUM(s.sales) sales
FROM plch_sales s
GROUP BY ROLLUP(s.product, s.country)
ORDER BY s.product, s.country;
  • ROLLUP(s.product, s.country) will create totals for (product,country) and (product) and grand total. It will not create the (country) total.

    cube(A,B) :totals for (A)/(B)/(A,B)/grand total
    rollup(A,B) :totals for (A,B)/(A)/grand total

Choice 4 √

SELECT CASE GROUPING_ID(s.product, s.country)
         WHEN 1 THEN 'Product'
         WHEN 2 THEN 'Country'
         WHEN 3 THEN 'Grand'
       END total
     , CASE GROUPING(s.product)
         WHEN 1 THEN 'TOTAL'
         ELSE s.product
       END product
     , CASE GROUPING(s.country)
         WHEN 1 THEN 'TOTAL'
         ELSE s.country
       END country
     , SUM(s.sales) sales
FROM plch_sales s
GROUP BY GROUPING SETS(
   (s.product),
   (s.country),
   ()
)
ORDER BY s.product, s.country;
  • GROUPING SETS allow you to easily define several combinations you wish to GROUP BY.
  • It creates all three GROUP BY combinations and does a UNION ALL on the results.
  • 题目一已经遇到过,不多说

Choice 5 √

SELECT CASE GROUPING_ID(s.product, s.country)
         WHEN 1 THEN 'Product'
         WHEN 2 THEN 'Country'
         WHEN 3 THEN 'Grand'
       END total
     , CASE GROUPING(s.product)
         WHEN 1 THEN 'TOTAL'
         ELSE s.product
       END product
     , CASE GROUPING(s.country)
         WHEN 1 THEN 'TOTAL'
         ELSE s.country
       END country
     , SUM(s.sales) sales
FROM plch_sales s
GROUP BY CUBE(s.product, s.country)
HAVING GROUPING_ID(s.product, s.country) > 0
ORDER BY s.product, s.country;
  • CUBE creates all 4 possible GROUP BY combinations. HAVING GROUPING_ID removes the (product,country) combination.
  • 这四种情况分别是:(product,country)00、(country)10、(product)01、grand total 11;GROUPING_ID > 0排除了(product,country);符合题意。

Choice 6 ×

SELECT CASE GROUPING_ID(product, country)
         WHEN 1 THEN 'Product'
         WHEN 2 THEN 'Country'
         WHEN 3 THEN 'Grand'
       END total
     , CASE GROUPING(product)
         WHEN 1 THEN 'TOTAL'
         ELSE product
       END product
     , CASE GROUPING(country)
         WHEN 1 THEN 'TOTAL'
         ELSE country
       END country
     , SUM(sales) sales
FROM plch_sales
GROUP BY GROUPING SETS(
   (product),
   (country),
   ()
)
ORDER BY product, country;
  • This would be fine - except the ORDER BY clause will order by product/country aliases, thus country TOTAL comes before US.
  • 与选项一一样的错误,排序后不符合题目要求

Background

  • GROUP BY子句中的表达式可以包含FROM子句中的表,视图或实例化视图的任何列,无论这些列是否出现在select list中
  • GROUP BY子句对行进行分组后,排序需要自行 order by。

Explaination

GROUP BY GROUPING SETS(
   (product),
   (country),
   ()
)
  1. In the GROUPING SETS clause three sets are defined here: (s.product), (s.country) and (), which actually creates a set of aggregates GROUP BY s.product, a set of aggregates GROUP BY s.country, and finally a set of aggregates with no GROUP BY (a grand total.)

  2. GROUPING SETS is a generalization of ROLLUP and CUBE that also can be used to create several aggregations. ROLLUP is an easy shortcut for getting normal “subtotals” for a report, CUBE gets all combinations.

  3. GROUP BY ROLLUP(s.product, s.country) is like doing GROUPING SETS ( (s.product,s.country), (s.product), () )

  4. GROUP BY CUBE(s.product, s.country) is like doing GROUPING SETS ( (s.product,s.country), (s.product), (s.country), () )

  5. To determine if s.product / s.country is null because the data is null or is null because it is a subtotal, the function GROUPING returns 1 if it is a subtotal, otherwise 0. GROUPING_ID is a bitmap of GROUPING - GROUPING_ID(s.product,s.country) is the same as 1GROUPING(s.product)+2GROUPING(s.country).


示例🔗

Oracle documentation example of GROUPING SETS:

http://download.oracle.com/docs/cd/E11882_01/server.112/e17118/statements_10002.htm#i2091446

Examples by Bob Watkins:

http://www.techrepublic.com/article/group-by-grouping-sets-for-custom-rollups-in-oracle/6134424

Tom Kyte on GROUPING_ID and GROUPING SETS:

http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:37355353762363

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章