ROLLUP, CUBE, and GROUPING SETS in SQLAs reporting needs grow, regular GROUP BY is often not enough. For example, you may need to get all of the following at once:
You can write multiple queries and combine them with UNION ALL, but that is verbose and harder to maintain. SQL provides grouping extensions for these tasks: ROLLUP, CUBE, and GROUPING SETS.
Important: all practical examples in this lesson use SQL Server (AdventureWorks).
Syntax note: ROLLUP, CUBE, GROUPING SETS, and GROUPING() below are shown using SQL Server syntax. In MySQL, functionality is more limited and syntax is partly different (for example, WITH ROLLUP is commonly used, while CUBE and GROUPING SETS may be unavailable in classic form).
In this lesson, we will cover:
ROLLUP, CUBE, and GROUPING SETS differ;GROUPING().Advanced aggregation helps you:
Assume we have sales data in SalesOrderHeader with dimensions Status, CustomerID and metric TotalDue.
Regular GROUP BY returns only one grouping level. Extended grouping constructs return multiple levels at once.
ROLLUP: Hierarchical TotalsROLLUP builds a hierarchy from right to left in the column list.
GROUP BY ROLLUP (col1, col2, col3)
Generated levels:
(col1, col2, col3) - detail;(col1, col2) - subtotal over col3;(col1) - subtotal over col2 and col3;() - grand total.SELECT
Status,
CustomerID,
SUM(TotalDue) AS total_amount
FROM SalesOrderHeader
GROUP BY ROLLUP (Status, CustomerID)
ORDER BY Status, CustomerID;
Result:
Status + CustomerID pair;Status;CUBE: All Dimension CombinationsCUBE builds aggregates for all possible combinations of listed columns.
GROUP BY CUBE (col1, col2)
For two columns, levels are:
(col1, col2);(col1);(col2);().For three columns, combinations are already $2^3 = 8$, so result size can grow quickly.
SELECT
Status,
CustomerID,
SUM(TotalDue) AS total_amount
FROM SalesOrderHeader
GROUP BY CUBE (Status, CustomerID)
ORDER BY Status, CustomerID;
Result: besides detail and grand total, you also get:
Status;CustomerID.GROUPING SETS: Precise Control of LevelsGROUPING SETS lets you explicitly list only the grouping levels you need.
GROUP BY GROUPING SETS (
(col1, col2),
(col1),
()
)
SELECT
Status,
CustomerID,
SUM(TotalDue) AS total_amount
FROM SalesOrderHeader
GROUP BY GROUPING SETS (
(Status, CustomerID),
(Status),
()
)
ORDER BY Status, CustomerID;
This is equivalent to multiple GROUP BY ... UNION ALL ... queries, but is more compact and usually better optimized.
GROUPING()In generated total rows, dimension values often become NULL. The issue is that source data can also contain real NULL values.
GROUPING(column) helps distinguish them:
0 - regular value from source data;1 - value generated by aggregation level.SELECT
Status,
CustomerID,
SUM(TotalDue) AS total_amount,
GROUPING(Status) AS g_status,
GROUPING(CustomerID) AS g_customer
FROM SalesOrderHeader
GROUP BY ROLLUP (Status, CustomerID)
ORDER BY Status, CustomerID;
Practical report labeling pattern:
CASE
WHEN GROUPING(Status) = 1 AND GROUPING(CustomerID) = 1 THEN 'GRAND TOTAL'
WHEN GROUPING(CustomerID) = 1 THEN 'STATUS SUBTOTAL'
ELSE 'DETAIL'
END AS row_type
ROLLUP when you need hierarchical totals (for example, year -> month -> day).CUBE when you need all analytical slices across dimensions.GROUPING SETS when you want strict control over exactly which levels are returned.CUBE can increase row count significantly.DETAIL, SUBTOTAL, GRAND TOTAL) for readability.ORDER BY so totals appear in a predictable order.HAVING.Below is a MySQL example on the payment table using subtotals with WITH ROLLUP:
SELECT
staff_id,
customer_id,
SUM(amount) AS total_amount
FROM
payment
GROUP BY
staff_id, customer_id WITH ROLLUP
ORDER BY
GROUPING(staff_id),
staff_id,
GROUPING(customer_id),
customer_id;
In this query:
staff_id + customer_id pair;WITH ROLLUP adds subtotals per staff_id and a grand total;ORDER BY GROUPING(...) places rows in a convenient order: details, subtotals, then grand total.Important notes for MySQL:
WITH ROLLUP provides hierarchical totals, but not a full equivalent of CUBE/GROUPING SETS.UNION ALL.GROUPING(), sorting and total-row labeling are usually done with NULL checks.Order amount report by status and customer with totals:
ROLLUP (Status, CustomerID) gives detail, status subtotals, and grand total.
Multidimensional sales analysis:
CUBE (Status, CustomerID) gives all slice combinations across status and customer.
Custom order amount report:
GROUPING SETS lets you keep only the levels you need: detail + departmental subtotal + grand total.
ROLLUP, CUBE, and GROUPING SETS extend standard GROUP BY.ROLLUP creates hierarchical totals, CUBE creates all combinations, GROUPING SETS creates only explicitly listed levels.GROUPING() is essential for correctly interpreting generated total rows.By mastering these constructs, you can design more powerful SQL reports without long UNION ALL chains.