SQL Pivot and Unpivot — Transform Your SQL Data Views

Pivot and Unpivot

A teacher wants one class-summary row with counts for different mark bands, rather than separate rows per band.

Pivoting turns selected row categories into report columns. Unpivoting turns selected columns into rows, changing the representation rather than inventing new source facts.

  1. Explain and apply

    Reshape subject results between rows and columns and identify any aggregation or missing values.

  2. Work it out

    Create one summary row containing the strong and developing student counts.

  3. Avoid mistakes

    Hard-coded category columns need a maintenance policy when categories change.

Pivot and Unpivot: the request and its result

  1. School need

    A teacher wants one class-summary row with counts for different mark bands, rather than separate rows per band.

  2. Database operation

    Pivoting turns selected row categories into report columns. Unpivoting turns selected columns into rows, changing the representation rather than inventing new source facts.

  3. Check the outcome

    CASE turns each qualifying row into a one for its band. SUM combines those indicators into two report columns.

A conceptual model. The query examples below show the actual syntax, input data and results.

Understand what the database does

Conditional aggregation uses CASE to choose which values contribute to each aggregate column. SQL Server also has PIVOT and UNPIVOT operators, while their availability and syntax differ elsewhere. UNION ALL can build a column-to-row transformation while retaining duplicates.

Related concepts: Choose Values with CASE and Group and Summarize and Remove Duplicate Results.

What is Pivot?

Pivot converts row data into column format. It's commonly used to create summary reports where each unique value in a column becomes a new column itself.

Sample Table: marks

SQL · portable concepts; preserved examples identify their engine

CREATE TABLE marks (
  roll_no INT,
  name VARCHAR(50),
  subject VARCHAR(20),
  marks INT
);

INSERT INTO marks VALUES
(1, 'Aarav', 'Maths', 90),
(1, 'Aarav', 'Science', 85),
(1, 'Aarav', 'English', 88),
(2, 'Diya', 'Maths', 80),
(2, 'Diya', 'Science', 92),
(2, 'Diya', 'English', 91);

Pivoting Using CASE + Aggregation

Input definitions and data for this example. The shown statements establish the base fixture. Where this section changes an existing object, its required object definition is included below; use the stated before/after table to follow a sequence of changes.

CREATE TABLE marks (
  roll_no INT,
  name VARCHAR(50),
  subject VARCHAR(20),
  marks INT
);

INSERT INTO marks VALUES
(1, 'Aarav', 'Maths', 90),
(1, 'Aarav', 'Science', 85),
(1, 'Aarav', 'English', 88),
(2, 'Diya', 'Maths', 80),
(2, 'Diya', 'Science', 92),
(2, 'Diya', 'English', 91);

SQL · portable concepts; preserved examples identify their engine

Not all databases support PIVOT natively, so we use CASE WHEN to simulate it.

SELECT
  roll_no,
  name,
  MAX(CASE WHEN subject = 'Maths' THEN marks END) AS Maths,
  MAX(CASE WHEN subject = 'Science' THEN marks END) AS Science,
  MAX(CASE WHEN subject = 'English' THEN marks END) AS English
FROM marks
GROUP BY roll_no, name;
roll_no | name  | Maths | Science | English
--------+-------+-------+---------+--------
1       | Aarav | 90    | 85      | 88
2       | Diya  | 80    | 92      | 91

What is Unpivot?

Unpivot is used to transform columns into rows — useful when normalizing denormalized reports or preparing data for analysis tools.

Unpivot Using UNION ALL

Input definitions and data for this example. The shown statements establish the base fixture. Where this section changes an existing object, its required object definition is included below; use the stated before/after table to follow a sequence of changes.

CREATE TABLE marks (
  roll_no INT,
  name VARCHAR(50),
  subject VARCHAR(20),
  marks INT
);

INSERT INTO marks VALUES
(1, 'Aarav', 'Maths', 90),
(1, 'Aarav', 'Science', 85),
(1, 'Aarav', 'English', 88),
(2, 'Diya', 'Maths', 80),
(2, 'Diya', 'Science', 92),
(2, 'Diya', 'English', 91);

SQL · portable concepts; preserved examples identify their engine

-- Unpivot the above pivoted result
SELECT roll_no, name, 'Maths' AS subject, Maths AS marks FROM pivoted_table
UNION ALL
SELECT roll_no, name, 'Science', Science FROM pivoted_table
UNION ALL
SELECT roll_no, name, 'English', English FROM pivoted_table;

If your SQL dialect supports UNPIVOT (like Oracle):

SELECT roll_no, name, subject, marks
FROM pivoted_table
UNPIVOT (
  marks FOR subject IN (Maths, Science, English)
);

Real-World School Use Cases

  • 📄 Displaying subject-wise marks in a single row for each student
  • Transforming term-based scores into columnar formats for comparison
  • Importing Excel-like formats into normalized tables
  • Generating analytics dashboards where subjects are shown as categories

Best Practices

Query discussed in this section:

SELECT roll_no, name, subject, marks
FROM pivoted_table
UNPIVOT (
  marks FOR subject IN (Maths, Science, English)
);
  • Always use GROUP BY when pivoting with aggregation
  • Use NULL handling for missing data in unpivoted views
  • Normalize data before storing; pivot/unpivot only for display/reporting

Important differences and mistakes

Hard-coded category columns need a maintenance policy when categories change. NULL handling can differ between an engine’s UNPIVOT operator and a hand-written UNION ALL. Decide the result’s row identity before grouping so multiple assessments are not unintentionally collapsed.

Try it with the school data

Create one summary row containing the strong and developing student counts.

This practice uses a separate, fresh fixture named practice_students. It does not continue the changing table state of the earlier examples.

Practice input: practice_students
roll_nonameclassmarkscity
1Aarav10A85Pune
2Diya9B70Chennai
3Sneha10A85Pune

SQL · create only these practice tables in a disposable database:

CREATE TABLE practice_students (roll_no INTEGER PRIMARY KEY, name VARCHAR(50), class VARCHAR(10), marks INTEGER, city VARCHAR(30));
INSERT INTO practice_students VALUES
(1, 'Aarav', '10A', 85, 'Pune'),
(2, 'Diya', '9B', 70, 'Chennai'),
(3, 'Sneha', '10A', 85, 'Pune');

Worked answer and reasoning

This practice uses a separate, fresh fixture named practice_students. It does not continue the changing table state of the earlier examples.

Practice input: practice_students
roll_nonameclassmarkscity
1Aarav10A85Pune
2Diya9B70Chennai
3Sneha10A85Pune

SQL · create only these practice tables in a disposable database:

CREATE TABLE practice_students (roll_no INTEGER PRIMARY KEY, name VARCHAR(50), class VARCHAR(10), marks INTEGER, city VARCHAR(30));
INSERT INTO practice_students VALUES
(1, 'Aarav', '10A', 85, 'Pune'),
(2, 'Diya', '9B', 70, 'Chennai'),
(3, 'Sneha', '10A', 85, 'Pune');

SQL · this practice variation runs with SQLite 3; the preserved examples retain their stated dialect

SELECT SUM(CASE WHEN marks >= 80 THEN 1 ELSE 0 END) AS strong_students, SUM(CASE WHEN marks < 80 THEN 1 ELSE 0 END) AS developing_students FROM practice_students;
Expected query result
strong_studentsdeveloping_students
21

CASE turns each qualifying row into a one for its band. SUM combines those indicators into two report columns.

Check your understanding

What must be decided before pivoting several marks per student?
Choose one answer. You can try again.

Answer and explanation

Which key and aggregation rule define one intended output row.

CASE turns each qualifying row into a one for its band. SUM combines those indicators into two report columns.

Use the idea independently

Pivoting turns selected row categories into report columns. Unpivoting turns selected columns into rows, changing the representation rather than inventing new source facts.

CASE turns each qualifying row into a one for its band. SUM combines those indicators into two report columns.

Continue your understanding with Choose Values with CASE and Group and Summarize and Remove Duplicate Results.

Source for the engine-specific behavior: MySQL 8.4 reference documentation.

Reaching the recap and completing the quizzes are separate milestones.

    Next lesson: SQL Recursive Queries — Solve Hierarchies and Sequences with SQL →