Skip to main content

VillageSQL is a drop-in replacement for MySQL with extensions.

All examples on this page work on VillageSQL. Install Now →
GROUP BY gives you 1 row per group and no row for the whole. WITH ROLLUP adds that row, and the subtotals in between when you group on several columns. It saves running a second query and adding the numbers up yourself. Open the client with mysql -u root -p sakila before you start.

How WITH ROLLUP works

Replace GROUPING_COLUMN with the column that defines the groups, TABLE_NAME with the table, and AGGREGATE_FUNCTION(COLUMN_NAME) with what to work out per group. WITH ROLLUP goes at the end of the GROUP BY clause. The server returns the normal groups, then extra rows where a grouping column is set aside and its aggregate covers everything. It puts NULL in the columns it set aside, which is how you spot those rows.

Examples

1) Add a total row

6 rows rather than 5. The last one has NULL where a rating would be, and its count is the whole table.

2) Subtotals as well

Group on 2 columns and you get a subtotal per value of the first, then the grand total.
Read a row by which columns are NULL. G / NULL / 3 is the subtotal for G across every rate. NULL / NULL / 10 is the grand total, which matches the 10 films that run 185 minutes. The rollup rows follow the groups they summarise rather than collecting at the bottom, so the result reads top to bottom as a report.

Telling a rollup NULL from a real one

The NULL in a rollup row means “every value of this column”. A NULL in the data means “no value recorded”. They print identically, so on a column that can be NULL you cannot tell the 2 apart by looking. GROUPING() answers the question directly. It returns 1 when the column was set aside for that row and 0 otherwise.
rating is never NULL in Sakila, so here the 2 questions have the same answer. On a column that does hold NULLs, testing rating IS NULL would catch both kinds of row and GROUPING(rating) = 1 catches only the rollup.

ORDER BY moves the total, and changes the sort

The examples above have no ORDER BY, which is deliberate: WITH ROLLUP puts each rollup row directly under the groups it summarises, and sorting throws that arrangement away.
2 things moved. The grand total sorted to the top, because NULL sorts before every real value. And the ratings came out alphabetically rather than in the ENUM declared order that GROUP BY showed, because sorting a rollup result sorts it as text. So decide which you want. Leave ORDER BY off for a report that reads as subtotals under their groups, or add it and accept that the rollup rows land wherever the sort puts them.

Labelling the rollup rows

A column of NULLs is not what you want in a report. COALESCE returns its first argument that is not NULL, so it puts a word there instead.
That reads well and it is only safe here because rating is never NULL in the data. On a column that can be NULL, COALESCE would label real gaps as totals, so use GROUPING() in the test instead.

Summary

  • Add WITH ROLLUP to the end of GROUP BY to get subtotal and total rows.
  • Read NULL in a grouping column as “every value of this column”.
  • Expect a subtotal per value of each earlier column when you group on several.
  • Use GROUPING(column) to tell a rollup row from a row where the data is NULL.
  • Expect ORDER BY to move the rollup rows and to sort an ENUM as text.

See also