Applied Oracle SQL Aggregation! Re-aggregating GROUP BY Results and ROLLUP

English

“I was able to calculate the total salary for each department. But how do I get the average of those department totals?” When performing aggregation processing in Oracle Database, there are times when you want to use AVG or MAX on the results already grouped by GROUP BY.

This article explains techniques for further aggregating aggregation results (using subqueries and nested aggregate functions) and using ROLLUP to automatically calculate subtotals and grand totals, using the EMP table from the SCOTT schema.

You can import and use the SCOTT sample schema by executing the following:

$ sqlplus / as sysdba
SQL> @?/rdbms/admin/utlsampl.sql
$ sqlplus scott/tiger

Conclusion & Methods List

There are two main ways to further aggregate aggregation results:

  1. Use a Subquery: Treat the aggregation result as a temporary table in the FROM clause (Standard method).
  2. Nest Aggregate Functions: Layer functions like AVG(SUM(SAL)) (Oracle proprietary feature).

Also, ROLLUP is optimal for outputting a total line at once.

[What you will learn in this article]

  • SQL to find the “Average” or “Maximum” of totals by department.
  • How to output a “Grand Total” line in a single query using ROLLUP.
  • Handling NULLs using NVL.

1. Prerequisite Data: Checking the EMP Table

This time, we use the Oracle standard scott.emp table. First, let’s check the contents of the data and the target to be aggregated.

SELECT EMPNO, ENAME, JOB, SAL, DEPTNO FROM EMP ORDER BY DEPTNO;

Result:

SQL> SELECT EMPNO, ENAME, JOB, SAL, DEPTNO FROM EMP ORDER BY DEPTNO;

EMPNO ENAME JOB SAL DEPTNO
---------- ---------- --------- ---------- ----------
7782 CLARK MANAGER 2450 10
7839 KING PRESIDENT 5000 10
7934 MILLER CLERK 1300 10
7566 JONES MANAGER 2975 20
7902 FORD ANALYST 3000 20
7876 ADAMS CLERK 1100 20
7369 SMITH CLERK 800 20
7788 SCOTT ANALYST 3000 20
7521 WARD SALESMAN 1250 30
7844 TURNER SALESMAN 1500 30
7499 ALLEN SALESMAN 1600 30
7900 JAMES CLERK 950 30
7698 BLAKE MANAGER 2850 30
7654 MARTIN SALESMAN 1250 30

14 rows selected.

Data Points:

  • DEPTNO (Department Number): 3 departments exist: 10, 20, and 30.
  • SAL (Salary): Salary of each employee.

Goal: First, sum up SAL for each department, and then analyze those figures further.


2. Step 1: Basic GROUP BY (Aggregation by Department)

First, calculate the basic “Total Salary by Department.”

SELECT 
    DEPTNO,
    SUM(SAL) AS DEPT_TOTAL
FROM EMP
GROUP BY DEPTNO;

Execution Result:

SQL> SELECT
2 DEPTNO,
3 SUM(SAL) AS DEPT_TOTAL
4 FROM EMP
5 GROUP BY DEPTNO;

DEPTNO DEPT_TOTAL
---------- ----------
30 9400
10 8750
20 10875

These three numbers “9400, 10875, 8750” become the input data for the next step.


3. Step 2: Further Aggregating Aggregation Results

This is the main topic. We will calculate the average value of the “Total Salary by Department (3 numbers).”

If the result is (9400 + 10875 + 8750) ÷ 3 = 9675, it is correct.

Method A: Using a Subquery (Inline View)

This is the most versatile method and works with other databases (MySQL, PostgreSQL, etc.). Place the SQL from Step 1 inside the FROM clause.

SELECT 
    AVG(DEPT_TOTAL) AS AVG_OF_TOTALS
FROM 
    (SELECT DEPTNO, SUM(SAL) AS DEPT_TOTAL
     FROM EMP
     GROUP BY DEPTNO);

Explanation:

The inner SELECT creates 3 rows of aggregation results, and the outer SELECT averages those 3 rows.

Method B: Nesting Aggregate Functions (Oracle Feature)

In Oracle Database, you can nest aggregate functions. It can be written with very short code.

SELECT 
    AVG(SUM(SAL)) AS AVG_OF_TOTALS
FROM EMP
GROUP BY DEPTNO;

Execution Result (Common to A/B):

SQL> SELECT
2 AVG(DEPT_TOTAL) AS AVG_OF_TOTALS
3 FROM
4 (SELECT DEPTNO, SUM(SAL) AS DEPT_TOTAL
5 FROM EMP
6 GROUP BY DEPTNO);

AVG_OF_TOTALS
-------------
9675

SQL> SELECT
2 AVG(SUM(SAL)) AS AVG_OF_TOTALS
3 FROM EMP
4 GROUP BY DEPTNO;

AVG_OF_TOTALS
-------------
9675

Note:

When nesting aggregate functions (e.g., AVG(SUM(SAL))), you cannot display grouping columns such as DEPTNO in the SELECT clause (because displaying individual department numbers while calculating the overall average is logically contradictory).


4. Step 3: Automatically Adding “Grand Total” with ROLLUP

“I want to output the total by department, and also list the grand total for the whole company at the end.”

To achieve this, you don’t need to connect two SQL queries with UNION ALL. Using the ROLLUP option solves it in one go.

SELECT 
    DEPTNO,
    SUM(SAL) AS TOTAL_SAL
FROM EMP
GROUP BY ROLLUP(DEPTNO);

Execution Result:

SQL> SELECT
2 DEPTNO,
3 SUM(SAL) AS TOTAL_SAL
4 FROM EMP
5 GROUP BY ROLLUP(DEPTNO);

DEPTNO TOTAL_SAL
---------- ----------
10 8750
20 10875
30 9400
29025

Explanation:

Just by writing GROUP BY ROLLUP(DEPTNO), Oracle automatically adds an “aggregated row without grouping (Grand Total)” at the end. The row where DEPTNO is NULL represents the grand total.


5. Application: Cleaning Up the Appearance (Handling NULL)

If you are bothered by DEPTNO becoming (null) in the ROLLUP result, using the NVL or DECODE function to replace it with a string like “Total” improves the quality as a report.

SELECT 
    DECODE(GROUPING(DEPTNO), 1, 'Total', DEPTNO) AS DEPT_LABEL,
    SUM(SAL) AS TOTAL_SAL
FROM EMP
GROUP BY ROLLUP(DEPTNO);

Execution Result:

SQL> SELECT
2 DECODE(GROUPING(DEPTNO), 1, 'Total', DEPTNO) AS DEPT_LABEL,
3 SUM(SAL) AS TOTAL_SAL
4 FROM EMP
5 GROUP BY ROLLUP(DEPTNO);

DEPT_LABEL TOTAL_SAL
---------------------------------------- ----------
10 8750
20 10875
30 9400
Total 29025

Explanation:

  • GROUPING(DEPTNO): A function that returns 1 if it is an aggregated row (row created by ROLLUP) and 0 if it is normal data.
  • Using this, we display “‘Total’ if it is an aggregated row.”
  • Note: Since DEPTNO is a numeric type, you might need TO_CHAR conversion to mix in the string ‘Total’, but here it is introduced as a concept.

6. Troubleshooting & Notes

ItemNoteSolution
Aggregating NULL DataSUM and AVG ignore NULL.If you want to treat NULL as 0, convert it like SUM(NVL(COMM, 0)).
Displaying Columns when NestedSELECT DEPTNO, AVG(SUM(SAL))... causes an error (ORA-00937).When nesting aggregation, remove the grouping key (DEPTNO) from the SELECT clause.
Data TypeDo not SUM non-numeric columns (like names).Check the column definition of the aggregation target.

7. FAQ: Frequently Asked Questions

Q1. What is the difference between GROUP BY ROLLUP and CUBE?

A. ROLLUP performs hierarchical aggregation (e.g., Year > Month > Day > Grand Total), while CUBE outputs every combination of specified columns (cross-tabulation). If you just want a simple grand total, ROLLUP is sufficient.

Q2. What happens to the ROLLUP grand total if filtered by a WHERE clause?

A. The grand total is calculated only using the data remaining after filtering by the WHERE clause. Excluded data is not included in the grand total.

Q3. Can aggregate functions be nested 3 levels deep?

A. No, even in Oracle, nesting of aggregate functions is limited to 2 levels (e.g., MAX(AVG(SUM(SAL))) is not allowed). If you need more, please use subqueries.


Summary

  1. Re-aggregation: If you want to further aggregate the results of GROUP BY, use a Subquery or Nested Aggregate Functions AVG(SUM(col)).
  2. Grand Total: Using ROLLUP instead of UNION is smart.
  3. NULL: NULL display in aggregated rows can be controlled with functions like GROUPING.

This is a technique that significantly reduces the man-hours for creating summary reports. Please utilize it in your actual work.

This article explains using Oracle Database 19c (screens and default values may differ in other versions).

[reference]
Oracle Database SQL Language Reference, 19c

コメント

Copied title and URL