RecipesPivot without PIVOT
Pivot without PIVOT
Turn row values into columns with conditional aggregation — one query that runs the same on every engine.
The problem
You have one row per (customer, status) and you want one row per customer with a
column for each status. Some engines have PIVOT; its syntax differs everywhere
and SQLite / MySQL don't have it at all.
The pattern: CASE inside an aggregate
One SUM(CASE …) per output column. GROUP BY the thing you want as rows.
SQL playground
Loading editor…
⌘/Ctrl + Enter
- Use
SUMfor money/counts,MAXfor "did this row exist" flags,COUNTfor tallies. ELSE 0keeps sums numeric;ELSE NULL(the default) is fine forMAX.COUNT(CASE WHEN … THEN 1 END)counts matches — theELSEis left out on purpose so non-matches areNULLand skipped.
Counting instead of summing
SQL playground
Loading editor…
⌘/Ctrl + Enter
Filtered aggregates (Postgres, SQLite 3.30+)
Cleaner than CASE where supported:
SELECT
customer_id,
SUM(amount) FILTER (WHERE status = 'paid') AS paid,
SUM(amount) FILTER (WHERE status = 'refunded') AS refunded
FROM orders
GROUP BY customer_id;Gotchas
- The columns are hard-coded. You have to know the values in advance. Dynamic pivots need generated SQL or a client-side crosstab.
- Unpivot (columns back to rows) is the reverse:
UNION ALLoneSELECTper source column, orCROSS JOINa small values list.