And again, for more than a few “data types," I suggest using crosstab() :
SELECT * FROM crosstab( $$SELECT DISTINCT ON (1, 2) 'max' AS "type", data_type, val FROM tbl ORDER BY 1, 2, val DESC$$ ,$$VALUES ('Final Fantasy'), ('Quake 3'), ('World of Warcraft')$$) AS x ("type" text, "Final Fantasy" int, "Quake 3" int, "World of Warcraft" int)
Return:
type | Final Fantasy | Quake 3 | World of Warcraft -----+---------------+---------+------------------- max | 500 | 1500 | 1200
More explanation for the basics:
PostgreSQL Cross Forward Request
Dynamic solution
The difficulty is to make it fully dynamic: for it to work for
- unknown number of columns (data_types in this case)
- with unknown names (data_types again)
At least the type is well known: integer in this case.
In short: this is not possible with the current PostgreSQL (including 9.3). There are approximations with polymorphic types and ways to circumvent restrictions using arrays or hstore types . Maybe enough for you. But it is strictly impossible to get the result with separate columns in a single SQL query. SQL is very hard on types and wants to know what to expect.
However , this can be done with two queries. The first builds the actual request for use. Based on the above simple case:
SELECT $f$SELECT * FROM crosstab( $$SELECT DISTINCT ON (1, 2) 'max' AS "type", data_type, val FROM tbl ORDER BY 1, 2, val DESC$$ ,$$VALUES ($f$ || string_agg(quote_literal(data_type), '), (') || $f$)$$) AS x ("type" text, $f$ || string_agg(quote_ident(data_type), ' int, ') || ' int)' FROM (SELECT DISTINCT data_type FROM tbl) x
This generates the query that you really need. Run the second inside the same transaction to avoid concurrency issues.
Note the strategic use of quote_literal() and quote_ident() to disinfect all kinds of illegal (for columns) names and prevent SQL injection.
Do not confuse multiple dollar-quoting layers . This is necessary to create dynamic queries. I put it as simple as possible.