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

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