Unpivot is the reverse of pivot: many columns become (key, value) rows. The portable form is UNION ALL, one branch per column — works in every dialect:
SELECT product, 'Q1' AS quarter, q1 AS amount FROM sales
UNION ALL SELECT product, 'Q2', q2 FROM sales
UNION ALL SELECT product, 'Q3', q3 FROM sales
UNION ALL SELECT product, 'Q4', q4 FROM sales;
A tidier version that reads the table once (PostgreSQL, DuckDB, and any engine with LATERAL):
-- PostgreSQL / DuckDB
SELECT s.product, v.quarter, v.amount
FROM sales s
CROSS JOIN LATERAL (VALUES
('Q1', s.q1), ('Q2', s.q2), ('Q3', s.q3), ('Q4', s.q4)
) AS v(quarter, amount);
BigQuery, Snowflake and DuckDB have a dedicated UNPIVOT operator:
-- BigQuery / Snowflake / DuckDB
SELECT product, quarter, amount
FROM sales
UNPIVOT (amount FOR quarter IN (q1, q2, q3, q4));
NULL handling.
UNION ALLkeeps rows where the value is NULL; theUNPIVOToperator excludes them by default (addINCLUDE NULLSto keep them). Pick the behaviour you want, especially when some quarters are empty.