U kunt dit doen met crosstab()
uit de extra module tablefunc:
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);
Als NULL
in plaats van 0
werkt ook, het kan gewoon SELECT *
. zijn in de buitenste vraag.
Gedetailleerde uitleg:
- PostgreSQL-kruistabelquery
De speciale "moeilijkheid" hier:geen werkelijke "waarde". Dus voeg 1 AS val
. toe als laatste kolom.
Onbekend aantal categorieën
Een volledig dynamische zoekopdracht (met onbekend resultaattype) is niet mogelijk in een enkele zoekopdracht. Je hebt twee nodig vragen. Bouw eerst dynamisch een instructie zoals hierboven en voer deze vervolgens uit. Details:
-
Meerdere max()-waarden selecteren met een enkele SQL-instructie
-
PostgreSQL kolommen naar rijen converteren? Transponeren?
-
Dynamisch kolommen genereren voor kruistabel in PostgreSQL
-
Dynamisch alternatief voor draaien met CASE en GROUP BY
Te veel categorieën
Als u het maximale aantal kolommen (1600) overschrijdt, is een klassieke kruistabel onmogelijk, omdat het resultaat niet kan worden weergegeven met afzonderlijke kolommen. (Ook zouden menselijke ogen nauwelijks een tabel met zoveel kolommen kunnen lezen)
Arrays of documenttypes zoals hstore
of jsonb
zijn het alternatief. Hier is een oplossing met 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;
-
Bouw het volledige waardenraster met:
(SELECT DISTINCT colb FROM matrix) b CROSS JOIN (SELECT DISTINCT cola FROM matrix) a
-
LEFT JOIN
bestaande combinaties, sorteer op het numerieke deel van de naam en aggregeer in arrays.right(colb, -1)::int
trimt het hoofdteken van 'A3' en giet de cijfers naar integer zodat we een juiste sorteervolgorde krijgen.
Basismatrix
Als je alleen een tabel van 0
. wilt een 1
waarbij x = y
, dit kan goedkoper:
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 voortbouwend op degene die je hebt opgegeven in de opmerkingen.
Merk op dat sqlfiddle.com momenteel een bug heeft die de weergave van arraywaarden vernietigt. Dus cast ik naar text
daar omheen te werken.