Multi-Column Grouping

RoadmapsSQL

Overview

When you GROUP BY two or more columns, the database creates one group for each unique COMBINATION of values across those columns. A single-column GROUP BY country gives one row per country; adding a second column, e.g. GROUP BY country, status, gives one row per (country, status) pair. Against the shared dataset: SELECT c.country, o.status, COUNT(*) AS n FROM customers c JOIN orders o ON o.customer_id = c.id GROUP BY c.country, o.status ORDER BY c.country, o.status; This produces one row for each (country, status) combination that exists in the data. US has both 'paid' and 'pending' orders, so US gets two rows. Think of multi-column grouping as creating a grid: each axis is a grouping column, each cell that has data becomes a result row.

Real-world reports almost always need more than one dimension — revenue by country AND year, count by department AND status. Multi-column grouping is how you produce these cross-tabulated summaries.

Where used: cross-tab and pivot reports, multi-dimensional analytics, breakdown-by dashboards

Why learn this

Query walkthrough

SELECT c.country, o.status, COUNT(*) AS n
FROM customers c
JOIN orders o ON o.customer_id = c.id
WHERE c.country IS NOT NULL
GROUP BY c.country, o.status
ORDER BY c.country, o.status;

Focus: Watch how 7 joined rows (after excluding Emma's NULL country) collapse into 5 groups when grouped by (country, status) — US splits into two rows because it has both paid and pending orders.

Common mistakes

Glossary

combination
A specific pairing or grouping of different values across multiple columns, like a unique country and status together.
dimension
A category or attribute used to organize and analyze data, like time, location, or product type.
summarisation
The process of condensing a large amount of detailed data into a shorter, aggregated form.

Recall questions

Understanding checks

What rows does this return?

AE | paid | 1 MX | paid | 1 UK | cancelled | 1 US | paid | 3 US | pending | 1

JOIN matches customers to their orders (excludes Greg/Hana with no orders). WHERE removes Emma (NULL country). The remaining 7 rows group by (country, status): AE-Farah has 1 paid; MX-Carlos has 1 paid; UK-Diana has 1 cancelled; US-paid has 3 (Alice orders 1,2 + Bob order 8); US-pending has 1 (Bob order 3).

If you changed this to GROUP BY category, price — how many rows would the result have?

6 rows — every product has a unique price, so each (category, price) combination maps to exactly one product. Multi-column grouping produces the same number of rows as the original table.

When the added grouping column has all unique values, every row becomes its own group. This demonstrates that adding high-cardinality columns to GROUP BY defeats summarisation.

Practice tasks

Add a second grouping dimension

This query counts orders by status only. Add customer_id as a second grouping column so you get one row per (customer_id, status) pair. Keep the ORDER BY matching the GROUP BY columns.

Challenge

Product category breakdown by order status

Write a query that shows each (category, status) combination with the total quantity of items ordered. Join products and order_items and orders. Order by category, then status.

Continue learning

Previous: GROUP BY

Previous: Aggregate Functions

Return to SQL Roadmap