When we have a query that returns a column with numerical data such as prices or population, we can use the following methods to automatically calculate the totals and subtotals within a category.
GROUP BY
with the WITH ROLLUP
Modifier
One way to calculate totals and subtotals is with the WITH ROLLUP
modifier of the GROUP BY
clause. When we use this modifier, we get a special row that automatically calculates totals and/or subtotals.
Here’s an example:
SELECT
IF(GROUPING(CountryCode), 'ALL COUNTRIES', CountryCode) AS CountryCode,
IF(GROUPING(District), 'ALL DISTRICTS', District) AS District,
IF(GROUPING(Name), 'ALL CITIES', Name) As CityName,
SUM(Population) AS Population
FROM City
WHERE CountryCode IN ('AUS', 'NZL')
GROUP BY CountryCode, District, Name WITH ROLLUP;
Result:
+---------------+-----------------+---------------+------------+ | CountryCode | District | CityName | Population | +---------------+-----------------+---------------+------------+ | AUS | Capital Region | Canberra | 322723 | | AUS | Capital Region | ALL CITIES | 322723 | | AUS | New South Wales | Central Coast | 227657 | | AUS | New South Wales | Newcastle | 270324 | | AUS | New South Wales | Sydney | 3276207 | | AUS | New South Wales | Wollongong | 219761 | | AUS | New South Wales | ALL CITIES | 3993949 | | AUS | Queensland | Brisbane | 1291117 | | AUS | Queensland | Cairns | 92273 | | AUS | Queensland | Gold Coast | 311932 | | AUS | Queensland | Townsville | 109914 | | AUS | Queensland | ALL CITIES | 1805236 | | AUS | South Australia | Adelaide | 978100 | | AUS | South Australia | ALL CITIES | 978100 | | AUS | Tasmania | Hobart | 126118 | | AUS | Tasmania | ALL CITIES | 126118 | | AUS | Victoria | Geelong | 125382 | | AUS | Victoria | Melbourne | 2865329 | | AUS | Victoria | ALL CITIES | 2990711 | | AUS | West Australia | Perth | 1096829 | | AUS | West Australia | ALL CITIES | 1096829 | | AUS | ALL DISTRICTS | ALL CITIES | 11313666 | | NZL | Auckland | Auckland | 381800 | | NZL | Auckland | Manukau | 281800 | | NZL | Auckland | North Shore | 187700 | | NZL | Auckland | Waitakere | 170600 | | NZL | Auckland | ALL CITIES | 1021900 | | NZL | Canterbury | Christchurch | 324200 | | NZL | Canterbury | ALL CITIES | 324200 | | NZL | Dunedin | Dunedin | 119600 | | NZL | Dunedin | ALL CITIES | 119600 | | NZL | Hamilton | Hamilton | 117100 | | NZL | Hamilton | ALL CITIES | 117100 | | NZL | Wellington | Lower Hutt | 98100 | | NZL | Wellington | Wellington | 166700 | | NZL | Wellington | ALL CITIES | 264800 | | NZL | ALL DISTRICTS | ALL CITIES | 1847600 | | ALL COUNTRIES | ALL DISTRICTS | ALL CITIES | 13161266 | +---------------+-----------------+---------------+------------+ 38 rows in set, 2 warnings (0.03 sec)
Here the WITH ROLLUP
modifier provided the numbers for the total and subtotals and we provided the labels.
We provided the labels by using a combination of the MySQL’s IF()
function and its GROUPING()
function. We specified ALL CITIES
and ALL DISTRICTS
for the subtotal rows, and ALL COUNTRIES
for the grand total at the end.
Note that the labels are optional, and our query could have been a lot simpler without them. If we didn’t add these labels, a NULL value would appear instead of the label (but the numbers would still appear).
See How to Add Totals and Subtotals When Using GROUP BY
in MySQL for a more detailed explanation of this approach.
Use a Window Function
Another way to calculate totals and subtotals is with a window function. This option allows us to present the totals and subtotals in their own column. So instead of having extra rows added to the result set for the totals and subtotals, we get extra columns.
Example:
SELECT
CountryCode AS "cc",
District,
Name AS "City Name",
Population AS "City Pop",
SUM(Population)
OVER (
PARTITION BY District
ORDER BY Population
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS "District Pop",
SUM(Population)
OVER (
PARTITION BY CountryCode
) AS "Country Pop",
SUM(Population)
OVER (
) AS "Total Pop"
FROM City
WHERE CountryCode IN ('AUS', 'NZL')
ORDER BY CountryCode;
Result:
+-----+-----------------+---------------+----------+--------------+-------------+-----------+ | cc | District | City Name | City Pop | District Pop | Country Pop | Total Pop | +-----+-----------------+---------------+----------+--------------+-------------+-----------+ | AUS | Capital Region | Canberra | 322723 | 322723 | 11313666 | 13161266 | | AUS | New South Wales | Wollongong | 219761 | 3993949 | 11313666 | 13161266 | | AUS | New South Wales | Central Coast | 227657 | 3993949 | 11313666 | 13161266 | | AUS | New South Wales | Newcastle | 270324 | 3993949 | 11313666 | 13161266 | | AUS | New South Wales | Sydney | 3276207 | 3993949 | 11313666 | 13161266 | | AUS | Queensland | Cairns | 92273 | 1805236 | 11313666 | 13161266 | | AUS | Queensland | Townsville | 109914 | 1805236 | 11313666 | 13161266 | | AUS | Queensland | Gold Coast | 311932 | 1805236 | 11313666 | 13161266 | | AUS | Queensland | Brisbane | 1291117 | 1805236 | 11313666 | 13161266 | | AUS | South Australia | Adelaide | 978100 | 978100 | 11313666 | 13161266 | | AUS | Tasmania | Hobart | 126118 | 126118 | 11313666 | 13161266 | | AUS | Victoria | Geelong | 125382 | 2990711 | 11313666 | 13161266 | | AUS | Victoria | Melbourne | 2865329 | 2990711 | 11313666 | 13161266 | | AUS | West Australia | Perth | 1096829 | 1096829 | 11313666 | 13161266 | | NZL | Auckland | Waitakere | 170600 | 1021900 | 1847600 | 13161266 | | NZL | Auckland | North Shore | 187700 | 1021900 | 1847600 | 13161266 | | NZL | Auckland | Manukau | 281800 | 1021900 | 1847600 | 13161266 | | NZL | Auckland | Auckland | 381800 | 1021900 | 1847600 | 13161266 | | NZL | Canterbury | Christchurch | 324200 | 324200 | 1847600 | 13161266 | | NZL | Dunedin | Dunedin | 119600 | 119600 | 1847600 | 13161266 | | NZL | Hamilton | Hamilton | 117100 | 117100 | 1847600 | 13161266 | | NZL | Wellington | Lower Hutt | 98100 | 264800 | 1847600 | 13161266 | | NZL | Wellington | Wellington | 166700 | 264800 | 1847600 | 13161266 | +-----+-----------------+---------------+----------+--------------+-------------+-----------+ 23 rows in set (0.00 sec)
When we use this method, we get duplicate values in the various columns, due to the fact that the countries and most districts contain multiple cities. In those cases where a district contains just one city, we don’t get any duplicates for the district column.