CASE
If your case is simple, as shown, the CASE statement will do:
SELECT year , sum(CASE WHEN animal = 'kittens' THEN price END) AS kittens , sum(CASE WHEN animal = 'puppies' THEN price END) AS puppies FROM ( SELECT year, animal, avg(price) AS price FROM tab_test GROUP BY year, animal HAVING count(*) > 2 ) t GROUP BY year ORDER BY year;
It doesn't matter if you use sum() , max() or min() as an aggregate function in an external query. All of them lead to the same value in this case.
SQL Fiddle
crosstab()
With more categories, this will be easier with the crosstab() query. It should also be faster for large tables.
You need to install the additional tablefunc module (once for each database). Since Postgres 9.1 is as simple as:
CREATE EXTENSION tablefunc;
Details in this related answer:
SELECT * FROM crosstab( 'SELECT year, animal, avg(price) AS price FROM tab_test GROUP BY animal, year HAVING count(*) > 2 ORDER BY 1,2' ,$$VALUES ('kittens'::text), ('puppies')$$) AS ct ("year" text, "kittens" numeric, "puppies" numeric);
There is no sqlfiddle for this, because the site does not allow additional modules.
Benchmark
To check my claims, I conducted a quick test with close to real data in my small test database. PostgreSQL 9.1.6. Test with EXPLAIN ANALYZE , the best of 10:
Test setup with 10020 lines:
CREATE TABLE tab_test (year int, animal text, price numeric); -- years with lots of rows INSERT INTO tab_test SELECT 2000 + ((g + random() * 300))::int/1000 , CASE WHEN (g + (random() * 1.5)::int) %2 = 0 THEN 'kittens' ELSE 'puppies' END , (random() * 200)::numeric FROM generate_series(1,10000) g; -- .. and some years with only few rows to include cases with count < 3 INSERT INTO tab_test SELECT 2010 + ((g + random() * 10))::int/2 , CASE WHEN (g + (random() * 1.5)::int) %2 = 0 THEN 'kittens' ELSE 'puppies' END , (random() * 200)::numeric FROM generate_series(1,20) g;
Results:
@bluefeet
Total Run Time: 95.401 ms
@wildplasser (different results include lines with count <= 3 )
Total Run Time: 64.497 ms
@Andreiy (+ ORDER BY )
& Amp; @ Erwin1 - CASE (both work roughly the same)
Total Run Time: 39.105 ms
@ Erwin2 - crosstab()
Total Run Time: 17.644 ms
A largely proportional (but irrelevant) result has only 20 lines. @Wildplasser CTE alone has a bit more overhead and spikes.
With more than a few lines, crosstab() quickly takes the lead. The @Andreiy query does much the same as my simplified version, the aggregate function in the external SELECT ( min() , max() , sum() ) does not produce a measurable difference (only two lines per group).
All as expected, no surprises, take my setup and try @home.