You can do this with crosstab() from the optional tablefunc module:
SELECT b , COALESCE(a1, 0) AS "A1" , COALESCE(a2, 0) AS "A2" , COALESCE(a3, 0) AS "A3" , ... -- all the way up to "A30" FROM crosstab( 'SELECT colb, cola, 1 AS val FROM matrix ORDER BY 1,2' , $$SELECT 'A'::text || g FROM generate_series(1,30) g$$ ) AS t (b text , a1 int, a2 int, a3 int, a4 int, a5 int, a6 int , a7 int, a8 int, a9 int, a10 int, a11 int, a12 int , a13 int, a14 int, a15 int, a16 int, a17 int, a18 int , a19 int, a20 int, a21 int, a22 int, a23 int, a24 int , a25 int, a26 int, a27 int, a28 int, a29 int, a30 int);
If NULL instead of 0 also works, it could just be SELECT * in the outer query.
Detailed explanation:
- PostgreSQL crosstab query
The special "difficulty" is here: no actual "meaning". So add 1 AS val as the last column.
Unknown number of categories
A fully dynamic query (with an unknown result type) is not possible in a single query. You need two queries. First create the statement as described above and execute it. Details:
Select multiple max () values ββusing a single SQL statement
Does PostgreSQL convert columns to rows? Transpose?
Dynamically create columns for a crosstab in PostgreSQL
A dynamic alternative to rotation with CASE and GROUP BY
Too many categories
If you exceed the maximum number of columns (1600) , a classic crosstab is not possible because the result cannot be represented by individual columns. (In addition, human eyes are unlikely to be able to read a table with so many columns)
Arrays or document types like hstore or jsonb are an alternative. Here is a solution with arrays:
SELECT colb, array_agg(cola) AS colas FROM ( SELECT colb, right(colb, -1)::int AS sortb , CASE WHEN m.cola IS NULL THEN 0 ELSE 1 END AS cola FROM (SELECT DISTINCT colb FROM matrix) b CROSS JOIN (SELECT DISTINCT cola FROM matrix) a LEFT JOIN matrix m USING (colb, cola) ORDER BY sortb, right(cola, -1)::int ) sub GROUP BY 1, sortb ORDER BY sortb;
Create a complete grid of values ββwith:
(SELECT DISTINCT colb FROM matrix) b CROSS JOIN (SELECT DISTINCT cola FROM matrix) a
LEFT JOIN existing combinations, arrange the numerical part of the name and aggregate into arrays.
right(colb, -1)::int truncates the leading character from "A3" and discards the digits to integers, so we get the correct sort order.
Base matrix
If you need a table 0 a 1 , where x = y , this might be cheaper:
SELECT x, array_agg((x = y)::int) AS y_arr FROM generate_series(1,10) x , generate_series(1,10) y GROUP BY 1 ORDER BY 1;
SQL Fiddle based on what you provided in the comments.
Please note that sqlfiddle.com currently has an error that kills the display of array values. So I applied text to get around it.