Example 1
Get per-page totals and per-device totals without their cross-product
GROUPING SETS ((page, device), (page)) runs two separate aggregations: one grouped by both page and device, and one grouped by page alone. The results are combined into a single output. In the (page) rows, device is NULL — a grouping placeholder — and the total is the sum across all devices for that page. ORDER BY page, GROUPING(device), COALESCE(device, '~~~') places detail rows before subtotals within each page: GROUPING(device) is 0 for detail rows and 1 for subtotal rows, and the COALESCE handles the NULL device in the subtotal rows without relying on engine-specific NULLS LAST syntax. Compared to ROLLUP(page, device), this query produces detail and page-subtotal rows but not a grand total, because GROUPING SETS lets you specify exactly which combinations you need.
CREATE TABLE web_events (
page VARCHAR(20),
device VARCHAR(20),
sessions INT
);
INSERT INTO
web_events
VALUES
('Home', 'Desktop', 500),
('Home', 'Mobile', 300),
('About', 'Desktop', 150),
('About', 'Mobile', 200);SELECT
page,
device,
SUM(sessions) AS total
FROM
web_events
GROUP BY
GROUPING SETS ((page, device), (page))
ORDER BY
page,
GROUPING(device),
COALESCE(device, '~~~');| page | device | total |
|---|---|---|
| About | Desktop | 150 |
| About | Mobile | 200 |
| About | NULL | 350 |
| Home | Desktop | 500 |
| Home | Mobile | 300 |
| Home | NULL | 800 |
Only PostgreSQL and SQL Server support GROUPING SETS natively. The syntax is identical between them.