I would use crosstab() for this. Install the optional tablefunc module if you do not already have it:
CREATE EXTENSION tablefunc
The basics are here:
PostgreSQL Cross Forward Request
How to work with additional columns:
Rotate on multiple columns using Tablefunc
Extended use:
A dynamic alternative to rotation with CASE and GROUP BY
Customization
CREATE TEMP TABLE tbl (id int, extra_info varchar(3), month date, value int); INSERT INTO tbl (id, extra_info, month, value) VALUES (1, 'abc', '2012-01-01', 10), (1, 'abc', '2012-02-01', 20), (2, 'def', '2012-01-01', 10), (2, 'def', '2012-02-01', 5), (1, 'abc', '2012-01-01', 15), (3, 'ghi', '2012-03-01', 15);
I am using the actual date in the base table, as I assume they just hide it to simplify your question. But with monthly names there would be no ORDER BY .
Query
SELECT * FROM crosstab( $$SELECT id, extra_info, to_char(month, 'mon'), sum(value) AS value FROM tbl GROUP BY 1,2,month ORDER BY 1,2,month$$ ,$$VALUES ('jan'::text), ('feb'), ('mar'), ('apr'), ('may'), ('jun') , ('jul'), ('aug'), ('sep'), ('oct'), ('nov'), ('dec')$$ ) AS ct (id int, extra text , jan int, feb int, mar int, apr int, may int, jun int , jul int, aug int, sep int, oct int, nov int, dec int);
Result:
id | extra | jan | feb | mar | apr | may | jun | jul | aug | sep | oct | nov | dec ----+-------+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+----- 1 | abc | 25 | 20 | | | | | | | | | | 2 | def | 10 | 5 | | | | | | | | | | 3 | ghi | | | 15 | | | | | | | | |
Installing the tablefunc module requires some overhead and some training, but the resulting queries are much faster and shorter and more universal.