By Fatskills Exam Guides Team — the exam nerds behind 28,500+ quizzes and 2.1M practice questions across 500+ global exams.
Pivot tables transform rows into columns, turning long, normalized data into a wide, aggregated format that’s easier to analyze. In SQL, you do this with CASE expressions (universal) or PIVOT (SQL Server, Oracle, PostgreSQL, BigQuery).
CASE
PIVOT
Why it matters in production:- Dashboards break without pivots. Most BI tools (Power BI, Tableau, Looker) expect data in a pivoted format. If you feed them raw transactional data, they’ll either fail or force you to pivot in the tool—slowing down reports.- Legacy systems demand it. You’ll inherit 20-year-old SQL queries that use CASE for pivots. If you don’t understand them, you’ll waste hours debugging.- Performance gains. Pivoting in SQL (not in Python/pandas) reduces data transfer and speeds up queries. A 10M-row table pivoted in SQL runs in seconds; pivoted in Python, it might crash your notebook.
Real-world scenario:You’re analyzing e-commerce sales. Your raw data has one row per order item:
order_id | product | category | revenue 1 | Laptop | Tech | 1200 1 | Mouse | Access | 50 2 | Monitor | Tech | 300
Your boss wants a monthly revenue report by category (Tech, Access, etc.). Without pivots, you’d write 10 separate queries. With pivots, you write one query and get:
month | Tech | Access | Total Jan | 1500 | 50 | 1550
CASE WHEN ... THEN ... ELSE ... END
Production insight: CASE is portable—works in every SQL dialect. Use it when you need fine-grained control (e.g., handling NULLs, custom aggregations).
PIVOT operator (SQL Server, Oracle, PostgreSQL, BigQuery)
Production insight: PIVOT is cleaner but less flexible than CASE. Some databases (MySQL) don’t support it—stick to CASE for cross-platform queries.
Aggregation function (SUM, COUNT, AVG)
SUM
COUNT
AVG
Production insight: Forgetting aggregation is the #1 cause of "duplicate rows" errors in pivots.
Column aliasing (AS)
AS
Production insight: Without aliases, your output columns will be named SUM(CASE WHEN ...)—unreadable in dashboards.
SUM(CASE WHEN ...)
GROUP BY
GROUP BY month
Production insight: Missing GROUP BY collapses all data into one row.
Dynamic vs. static pivots
Tech
Access
sales
order_id
order_date
product
category
revenue
Goal: Transform this:
order_id | order_date | category | revenue 1 | 2023-01-15 | Tech | 1200 2 | 2023-01-20 | Access | 50 3 | 2023-02-05 | Tech | 300
Into this:
month | Tech | Access | Total Jan | 1200 | 50 | 1250 Feb | 300 | NULL | 300
SELECT TO_CHAR(order_date, 'Mon') AS month, -- PostgreSQL -- EXTRACT(MONTH FROM order_date) AS month, -- BigQuery -- FORMAT(order_date, 'MMM') AS month, -- SQL Server category, revenue FROM sales;
SELECT TO_CHAR(order_date, 'Mon') AS month, SUM(CASE WHEN category = 'Tech' THEN revenue ELSE 0 END) AS Tech, SUM(CASE WHEN category = 'Access' THEN revenue ELSE 0 END) AS Access, SUM(revenue) AS Total FROM sales GROUP BY TO_CHAR(order_date, 'Mon') ORDER BY MIN(order_date); -- Ensures Jan comes before Feb
NULL
Total
Tech + Access
SELECT month, ISNULL(Tech, 0) AS Tech, -- Replace NULL with 0 ISNULL(Access, 0) AS Access, Tech + Access AS Total FROM ( SELECT FORMAT(order_date, 'MMM') AS month, category, revenue FROM sales ) AS SourceTable PIVOT ( SUM(revenue) FOR category IN ([Tech], [Access]) ) AS PivotTable ORDER BY MIN(order_date);
sql CREATE INDEX idx_sales_category ON sales(category);
sql WHERE order_date >= '2023-01-01' -- Filter before pivoting
sql WITH monthly_sales AS ( SELECT TO_CHAR(order_date, 'Mon') AS month, category, revenue FROM sales ) SELECT * FROM monthly_sales PIVOT (SUM(revenue) FOR category IN ([Tech], [Access]));
0
COALESCE
sql COALESCE(SUM(CASE WHEN ... END), 0) AS Tech
SUM()
COUNT()
tech
UPPER(category)
ISNULL
"You have a table employee_salaries with columns employee_id, department, and salary. Write a query to show the average salary per department, pivoted by gender (M/F)."
employee_salaries
employee_id
department
salary
Answer:
SELECT department, AVG(CASE WHEN gender = 'M' THEN salary ELSE NULL END) AS avg_male_salary, AVG(CASE WHEN gender = 'F' THEN salary ELSE NULL END) AS avg_female_salary FROM employee_salaries GROUP BY department;
Challenge:You have a table student_grades:
student_grades
student_id | subject | grade 1 | Math | 90 1 | Science | 85 2 | Math | 75
Write a query to pivot this into:
student_id | Math | Science 1 | 90 | 85 2 | 75 | NULL
Solution:
SELECT student_id, MAX(CASE WHEN subject = 'Math' THEN grade ELSE NULL END) AS Math, MAX(CASE WHEN subject = 'Science' THEN grade ELSE NULL END) AS Science FROM student_grades GROUP BY student_id;
Why it works: MAX() collapses multiple rows into one per student, ignoring NULL values.
MAX()
sql CASE WHEN condition THEN value ELSE default END
sql SELECT * FROM table PIVOT (SUM(column) FOR pivot_column IN ([value1], [value2])) AS PivotTable;
AVG()
COALESCE(column, 0)
ISNULL(column, 0)
Join 4M+ learners. Unlock unlimited quizzes, wrong-answer tracking, flashcards + reminders, study guides, and 1-on-1 challenges.