SQL Practice Problem

Pivot sales into month columns

Hard Analytics Patterns

Schema and sample data

Sales
sale_idregionmonthamount
1NorthJan120.00
2NorthFeb200.00
3NorthMar150.00
4SouthJan300.00
5SouthFeb250.00
6SouthMar400.00
7WestJan90.00
8WestFeb60.00
Show setup SQL (copy to run)
CREATE TABLE Sales (
    sale_id INT PRIMARY KEY,
    region  VARCHAR(20) NOT NULL,
    month   VARCHAR(3)  NOT NULL,
    amount  DECIMAL(10,2) NOT NULL
);

INSERT INTO Sales (sale_id, region, month, amount) VALUES
(1, 'North', 'Jan', 120.00),
(2, 'North', 'Feb', 200.00),
(3, 'North', 'Mar', 150.00),
(4, 'South', 'Jan', 300.00),
(5, 'South', 'Feb', 250.00),
(6, 'South', 'Mar', 400.00),
(7, 'West',  'Jan', 90.00),
(8, 'West',  'Feb', 60.00);
Your task

The Sales table stores one row per sale, with a region, a three letter month ('Jan', 'Feb' or 'Mar') and an amount. Return one row per region with a separate column for each month - Jan, Feb and Mar - holding that region total for the month. Sort the result by region.

Expected result

Your query should return
regionJanFebMar
North120.00200.00150.00
South300.00250.00400.00
West90.0060.00NULL
Show hint

Turning rows into columns is a pivot. The portable way is conditional aggregation: for each target column, sum the amount only for the rows that belong in it, using SUM(CASE WHEN month = 'Jan' THEN amount END), then GROUP BY region so you collapse to one row per region.

Try to write the query yourself before you open this.

Show solution and explanation
SELECT
    region,
    SUM(CASE WHEN month = 'Jan' THEN amount END) AS Jan,
    SUM(CASE WHEN month = 'Feb' THEN amount END) AS Feb,
    SUM(CASE WHEN month = 'Mar' THEN amount END) AS Mar
FROM Sales
GROUP BY region
ORDER BY region;

The idea is conditional aggregation, the most portable way to pivot rows into columns. Each CASE WHEN month = 'Jan' THEN amount END expression yields the amount when the row is a January sale and NULL otherwise. Wrapping it in SUM(...) adds up only the matching rows, because aggregate functions ignore NULL. Repeating that once per month gives one column per month, and GROUP BY region collapses the input to a single row per region.

Row by row: North totals 120, 200 and 150 for Jan, Feb and Mar; South totals 300, 250 and 400. West has no March sale, so SUM(CASE ...) for Mar sees no matching rows and returns NULL. If you would rather show a zero there, add an ELSE 0 inside the CASE or wrap the whole expression in COALESCE(SUM(...), 0). See pivot rows to columns for more on this pattern.

SQL Server also has a dedicated PIVOT operator that expresses the same result:

SELECT region, [Jan], [Feb], [Mar]\nFROM (SELECT region, month, amount FROM Sales) AS src\nPIVOT (SUM(amount) FOR month IN ([Jan], [Feb], [Mar])) AS pvt\nORDER BY region;

It is compact, but the month names must be hard coded in the IN list just like the CASE version, and the syntax is SQL Server specific. The conditional aggregation form works on every database and is easier to extend with extra logic, so many people reach for it first. A common mistake is to forget GROUP BY region, which either errors or, if you also select non aggregated columns incorrectly, returns one row per sale instead of one per region.

Keep practising

Work through more Hard exercises, or test yourself with the SQL interview questions.