Pivot rows into columns with FILTER
Turn row values into columns (for example monthly totals) using conditional aggregates, without crosstab.
- SQL
- GreenSnippets
- MIT
SELECT
customer_id,
SUM(amount) FILTER (WHERE status = 'paid') AS paid,
SUM(amount) FILTER (WHERE status = 'pending') AS pending,
SUM(amount) FILTER (WHERE status = 'refunded') AS refunded,
SUM(amount) AS total
FROM invoices
GROUP BY customer_id
ORDER BY total DESC;Example / expected output
-- customer_id paid pending refunded total
-- 7 1200 300 NULL 1500How it works
Conditional aggregation is portable and easy to read. In PostgreSQL the FILTER clause is the cleanest syntax; on other databases use SUM(CASE WHEN ... THEN amount END). The set of columns must be known when you write the query.
This snippet has been reviewed but was not executed by an automated test. Test it in your own environment before relying on it.
Related snippets
- SQL
Top N rows per group
Get the latest or highest N rows for each category using ROW_NUMBER in a subquery.
- SQL
Soft delete with a partial unique index
Keep deleted rows but still enforce unique emails among active rows using a partial unique index.
- SQL
Walk a tree with a recursive CTE
Query an employee/manager or category hierarchy to any depth using WITH RECURSIVE.
- SQL
Query JSONB and index it with GIN
Filter on JSONB fields with the containment operator and make it fast with a GIN index.

