Based on the SQLD exam syllabusReport an error
GROUPING SETS (set1, set2, …) aggregates only the sets you list and combines them. It is neither a hierarchy nor all combinations: only what you pick comes out.(DEPTNO, JOB) is one set, and () is the grand-total set.GROUPING(column) is 1 if the column was folded away by aggregation and became NULL in that row, and 0 if the row was grouped by an actual value. It separates an original NULL from a subtotal NULL.ROLLUP(A, B) = GROUPING SETS ((A, B), A, ()).Yesterday's CUBE(DEPTNO, JOB) produced 18 rows, including the 9 detail rows. If you only want to see department subtotals and job subtotals, the detail and the grand total are clutter. GROUPING SETS lets you list the sets yourself.
SELECT DEPTNO, JOB, SUM(SAL) AS SUM_SAL FROM EMP GROUP BY GROUPING SETS (DEPTNO, JOB) ORDER BY DEPTNO, JOB; DEPTNO JOB SUM_SAL
---------- --------- ----------
10 8750
20 10875
30 9400
ANALYST 6000
CLERK 4150
MANAGER 8275
PRESIDENT 5000
SALESMAN 5600GROUP BY DEPTNO and the result of GROUP BY JOB with UNION ALL. In each set, the column that was not used is NULL.GROUPING SETS (DEPTNO, JOB) is not an aggregation by (DEPTNO, JOB) combination. The comma inside the parentheses is a separator between sets.If a set has two or more columns, wrap them in parentheses. The empty parentheses () are the grand total, not grouped by any column.
SELECT DEPTNO, JOB, SUM(SAL) AS SUM_SAL FROM EMP GROUP BY GROUPING SETS ((DEPTNO, JOB), DEPTNO, ()) ORDER BY DEPTNO, JOB; DEPTNO JOB SUM_SAL
---------- --------- ----------
10 CLERK 1300
10 MANAGER 2450
10 PRESIDENT 5000
10 8750
20 ANALYST 6000
20 CLERK 1900
20 MANAGER 2975
20 10875
30 CLERK 950
30 MANAGER 2850
30 SALESMAN 5600
30 9400
29025ROLLUP(DEPTNO, JOB).| Notation | Sets | Rows |
|---|---|---|
GROUPING SETS (DEPTNO, JOB) | (DEPTNO), (JOB) | 8 |
GROUPING SETS ((DEPTNO, JOB)) | (DEPTNO, JOB) | 9 |
GROUPING SETS ((DEPTNO, JOB), ()) | (DEPTNO, JOB), () | 10 |
GROUPING SETS (DEPTNO, JOB, ()) | (DEPTNO), (JOB), () | 9 |
GROUPING SETS ((DEPTNO, JOB), DEPTNO, ()) | (DEPTNO, JOB), (DEPTNO), () | 13 |
GROUPING SETS ((DEPTNO, JOB)), with just one set listed, is the same as a plain GROUP BY DEPTNO, JOB.The sets do not even need to use the same columns. Here we aggregate department 10 once by job and once by manager.
SELECT DEPTNO, JOB, MGR, SUM(SAL) FROM EMP GROUP BY GROUPING SETS ((DEPTNO, JOB), (DEPTNO, MGR)) HAVING DEPTNO = 10 ORDER BY DEPTNO, JOB, MGR; DEPTNO JOB MGR SUM(SAL)
---------- --------- ---------- ----------
10 CLERK 1300
10 MANAGER 2450
10 PRESIDENT 5000
10 7782 1300
10 7839 2450
10 5000GROUPING SETS follows the GROUP BY rules as they are. If you SELECT a column that is in none of the sets, you get ORA-00979.
SELECT DEPTNO, JOB, SUM(SAL) AS SUM_SAL FROM EMP GROUP BY GROUPING SETS (DEPTNO, ());SELECT DEPTNO, JOB, SUM(SAL) AS SUM_SAL FROM EMP GROUP BY GROUPING SETS (DEPTNO, ())
*
ERROR at line 1:
ORA-00979: "JOB": must appear in the GROUP BY clause or be used in an aggregate functionWhen the NULLs in subtotal rows mix with NULLs in the original data, you cannot tell the two apart from the screen alone. If you aggregate department 10 with ROLLUP(JOB, MGR), PRESIDENT appears on two lines.
SELECT JOB, MGR, SUM(SAL) AS SUM_SAL, GROUPING(MGR) AS G_M FROM EMP WHERE DEPTNO = 10 GROUP BY ROLLUP(JOB, MGR) ORDER BY JOB, G_M, MGR;JOB MGR SUM_SAL G_M
--------- ---------- ---------- ----------
CLERK 7782 1300 0
CLERK 1300 1
MANAGER 7839 2450 0
MANAGER 2450 1
PRESIDENT 5000 0
PRESIDENT 5000 1
8750 1MGR IS NULL, because MGR is NULL on both.| GROUPING(column) | Meaning | Value seen in that column |
|---|---|---|
| 0 | A row grouped by the actual value of that column | The data value (NULL if it was NULL originally) |
| 1 | A subtotal or grand-total row aggregated by folding that column | NULL |
Looking at the GROUPING of both columns in a ROLLUP(DEPTNO, JOB) result reveals the kind of each row.
SELECT DEPTNO, JOB, SUM(SAL) AS SUM_SAL, GROUPING(DEPTNO) AS G_D, GROUPING(JOB) AS G_J FROM EMP GROUP BY ROLLUP(DEPTNO, JOB) ORDER BY DEPTNO, JOB; DEPTNO JOB SUM_SAL G_D G_J
---------- --------- ---------- ---------- ----------
10 CLERK 1300 0 0
10 MANAGER 2450 0 0
10 PRESIDENT 5000 0 0
10 8750 0 1
20 ANALYST 6000 0 0
20 CLERK 1900 0 0
20 MANAGER 2975 0 0
20 10875 0 1
30 CLERK 950 0 0
30 MANAGER 2850 0 0
30 SALESMAN 5600 0 0
30 9400 0 1
29025 1 1| G_D | G_J | Kind of row |
|---|---|---|
| 0 | 0 | Detail by department and job |
| 0 | 1 | Department subtotal |
| 1 | 1 | Grand total |
If you branch with CASE on the GROUPING value, you can put text in place of the blanks.
SELECT CASE GROUPING(DEPTNO) WHEN 1 THEN 'Total' ELSE TO_CHAR(DEPTNO) END AS DEPT_L, CASE WHEN GROUPING(DEPTNO) = 1 THEN ' ' WHEN GROUPING(JOB) = 1 THEN 'Subtotal' ELSE JOB END AS JOB_L, SUM(SAL) AS SUM_SAL FROM EMP GROUP BY ROLLUP(DEPTNO, JOB) ORDER BY GROUPING(DEPTNO), DEPTNO, GROUPING(JOB), JOB;DEPT_L JOB_L SUM_SAL
---------------------------------------- --------- ----------
10 CLERK 1300
10 MANAGER 2450
10 PRESIDENT 5000
10 Subtotal 8750
20 ANALYST 6000
20 CLERK 1900
20 MANAGER 2975
20 Subtotal 10875
30 CLERK 950
30 MANAGER 2850
30 SALESMAN 5600
30 Subtotal 9400
Total 29025TO_CHAR to put it in the same column as the text label. The THEN values of a CASE must have matching data types.DECODE does the same job. Here we attach an 'All' label to a CUBE result.
SELECT DECODE(GROUPING(DEPTNO), 1, 'All', DEPTNO) AS DEPT_L, DECODE(GROUPING(JOB), 1, 'All', JOB) AS JOB_L, SUM(SAL) AS SUM_SAL FROM EMP GROUP BY CUBE(DEPTNO, JOB) ORDER BY GROUPING(DEPTNO), DEPTNO, GROUPING(JOB), JOB;DEPT_L JOB_L SUM_SAL
---------------------------------------- --------- ----------
10 CLERK 1300
10 MANAGER 2450
10 PRESIDENT 5000
10 All 8750
20 ANALYST 6000
20 CLERK 1900
20 MANAGER 2975
20 All 10875
30 CLERK 950
30 MANAGER 2850
30 SALESMAN 5600
30 All 9400
All ANALYST 6000
All CLERK 4150
All MANAGER 8275
All PRESIDENT 5000
All SALESMAN 5600
All All 29025DECODE(GROUPING(DEPTNO), 1, 'All', DEPTNO), the first result value is a character string, so the numeric DEPTNO is converted to a string and returned.⚠️ Pitfall: If you give a label column the same alias as the original column name, ORDER BY reads that name as the alias. If you name it
AS JOBand useORDER BY GROUPING(JOB), the JOB inside GROUPING points to the alias that contains an aggregate expression, and ORA-00934 is raised (based on Oracle 23ai, the practice environment of this note). Give the label alias a different name.
SELECT DECODE(GROUPING(JOB), 1, 'Sum', JOB) AS JOB, SUM(SAL) AS SUM_SAL FROM EMP GROUP BY ROLLUP(JOB) ORDER BY GROUPING(JOB), JOB;SELECT DECODE(GROUPING(JOB), 1, 'Sum', JOB) AS JOB, SUM(SAL) AS SUM_SAL FROM EMP GROUP BY ROLLUP(JOB) ORDER BY GROUPING(JOB), JOB
*
ERROR at line 1:
ORA-00934: group function is not allowed hereGROUPING can also be used in HAVING. Use it to keep or remove only the subtotal rows.
SELECT DEPTNO, JOB, SUM(SAL) FROM EMP GROUP BY GROUPING SETS ((DEPTNO, JOB), DEPTNO, ()) HAVING GROUPING(JOB) = 1 ORDER BY DEPTNO; DEPTNO JOB SUM(SAL)
---------- --------- ----------
10 8750
20 10875
30 9400
29025ROLLUP and CUBE are shorthand for frequently used lists of sets. All of them can be expanded into GROUPING SETS.
| Original notation | Equivalent GROUPING SETS |
|---|---|
GROUP BY A, B | GROUPING SETS ((A, B)) |
ROLLUP(A) | GROUPING SETS (A, ()) |
ROLLUP(A, B) | GROUPING SETS ((A, B), A, ()) |
ROLLUP(A, B, C) | GROUPING SETS ((A, B, C), (A, B), A, ()) |
CUBE(A, B) | GROUPING SETS ((A, B), A, B, ()) |
A, ROLLUP(B) | GROUPING SETS ((A, B), A) |
ROLLUP((A, B)) | GROUPING SETS ((A, B), ()) |
This is the statement with CUBE(DEPTNO, JOB) expanded. The result is the same 18 rows as yesterday's CUBE.
SELECT DEPTNO, JOB, SUM(SAL) FROM EMP GROUP BY GROUPING SETS ((DEPTNO, JOB), (DEPTNO), (JOB), ()) ORDER BY DEPTNO, JOB; DEPTNO JOB SUM(SAL)
---------- --------- ----------
10 CLERK 1300
10 MANAGER 2450
10 PRESIDENT 5000
10 8750
20 ANALYST 6000
20 CLERK 1900
20 MANAGER 2975
20 10875
30 CLERK 950
30 MANAGER 2850
30 SALESMAN 5600
30 9400
ANALYST 6000
CLERK 4150
MANAGER 8275
PRESIDENT 5000
SALESMAN 5600
29025(DEPTNO), you may use or omit the parentheses.GROUPING SETS (DEPTNO, JOB), you cannot write that with ROLLUP or CUBE.Next week we cover window functions, which attach ranks and running values without reducing the number of rows.
Click a choice to reveal the answer and explanation.
Question 1
In EMP there are 3 departments, 5 jobs, and 9 (department, job) combinations. How many rows does the following SQL return? ```sql SELECT DEPTNO, JOB, SUM(SAL) FROM EMP GROUP BY GROUPING SETS (DEPTNO, JOB); ```
Question 2
In the result of SELECT DEPTNO, JOB, SUM(SAL), GROUPING(DEPTNO), GROUPING(JOB) FROM EMP GROUP BY ROLLUP(DEPTNO, JOB);, what are the values of GROUPING(DEPTNO) and GROUPING(JOB) in the grand-total row?
Question 3
Which of the following produces the same result as GROUP BY ROLLUP(DEPTNO, JOB)?
Question 4
When you aggregate department 10 with GROUP BY ROLLUP(JOB, MGR), the PRESIDENT row appears on two lines, and MGR is NULL on both. KING's MGR is NULL to begin with. Which is the correct way to pick out the job subtotal row of the two?
Question 5
In EMP there are 3 departments and 5 jobs. How many rows does the following SQL return? ```sql SELECT DEPTNO, JOB, SUM(SAL) FROM EMP GROUP BY GROUPING SETS (DEPTNO, JOB, ()); ```