Du kan gøre dette med crosstab()
fra det ekstra modul 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);
Hvis NULL
i stedet for 0
virker også, det kan bare være SELECT *
i den ydre forespørgsel.
Detaljeret forklaring:
- PostgreSQL krydstabulatorforespørgsel
Den særlige "besvær" her:ingen egentlig "værdi". Så tilføj 1 AS val
som sidste kolonne.
Ukendt antal kategorier
En fuldstændig dynamisk forespørgsel (med ukendt resultattype) er ikke mulig i en enkelt forespørgsel. Du skal bruge to forespørgsler. Byg først en sætning som ovenstående dynamisk, og kør den derefter. Detaljer:
-
Valg af flere max()-værdier ved hjælp af en enkelt SQL-sætning
-
PostgreSQL konvertere kolonner til rækker? Transponere?
-
Generer dynamisk kolonner til krydstabeller i PostgreSQL
-
Dynamisk alternativ til pivotering med CASE og GROUP BY
For mange kategorier
Hvis du overskrider det maksimale antal kolonner (1600), er en klassisk krydstabel umulig, fordi resultatet ikke kan repræsenteres med individuelle kolonner. (Også, menneskelige øjne ville næppe kunne læse en tabel med så mange kolonner)
Arrays eller dokumenttyper såsom hstore
eller jsonb
er alternativet. Her er en løsning med 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;
-
Byg det komplette gitter af værdier med:
(SELECT DISTINCT colb FROM matrix) b CROSS JOIN (SELECT DISTINCT cola FROM matrix) a
-
LEFT JOIN
eksisterende kombinationer, sorter efter den numeriske del af navnet og aggregér i arrays.right(colb, -1)::int
trimmer det førende tegn fra 'A3' og kaster cifrene til heltal, så vi får en korrekt sorteringsrækkefølge.
Grundlæggende matrix
Hvis du bare vil have en tabel med 0
en 1
hvor x = y
, dette kan fås billigere:
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 bygger på den, du har angivet i kommentarerne.
Bemærk, at sqlfiddle.com i øjeblikket har en fejl, der dræber visningen af array-værdier. Så jeg caster til text
der for at omgå det.