✏️ Explanatory Question
Level: Hard — A very common reporting request; MySQL lacks PIVOT, so you must know the conditional-aggregation trick.
PIVOT keyword like SQL Server. How do you transform rows into columns?
sales (row-based / "long" format)| product | month | amount |
|---|---|---|
| Laptop | Jan | 1000 |
| Laptop | Feb | 1500 |
| Laptop | Mar | 1200 |
| Phone | Jan | 800 |
| Phone | Feb | 950 |
| product | Jan | Feb | Mar |
|---|---|---|---|
| Laptop | 1000 | 1500 | 1200 |
| Phone | 800 | 950 | 0 |
CASE (or IF) with an aggregate like SUM(). Each future column becomes SUM(CASE WHEN month = 'Jan' THEN amount ELSE 0 END). The GROUP BY product collapses everything into one row per product.
SELECT
product,
SUM(CASE WHEN month = 'Jan' THEN amount ELSE 0 END) AS Jan,
SUM(CASE WHEN month = 'Feb' THEN amount ELSE 0 END) AS Feb,
SUM(CASE WHEN month = 'Mar' THEN amount ELSE 0 END) AS Mar
FROM sales
GROUP BY product
ORDER BY product;
-- IF() is MySQL-specific shorthand for CASE WHEN
SELECT
product,
SUM(IF(month = 'Jan', amount, 0)) AS Jan,
SUM(IF(month = 'Feb', amount, 0)) AS Feb,
SUM(IF(month = 'Mar', amount, 0)) AS Mar
FROM sales
GROUP BY product;
SUM correctly totals them. MAX(CASE ...) also works when there's exactly one value per cell, but SUM with ELSE 0 is the safer default for numeric pivots.
The big limitation is that you must know the column values (Jan, Feb, Mar) in advance. If months are dynamic (e.g., 12 months, or unknown categories), you need a dynamic pivot using prepared statements that build the SQL string.
-- Build the column list dynamically from the distinct months
SET @sql = NULL;
SELECT GROUP_CONCAT(
DISTINCT CONCAT(
'SUM(CASE WHEN month = ''', month, ''' THEN amount ELSE 0 END) AS `', month, '`'
)
) INTO @sql
FROM sales;
-- Assemble and run the full pivot query
SET @sql = CONCAT('SELECT product, ', @sql,
' FROM sales GROUP BY product ORDER BY product');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;