Du kan göra detta med crosstab()
från tilläggsmodulen 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);
Om NULL
istället för 0
fungerar också, det kan bara vara SELECT *
i den yttre frågan.
Detaljerad förklaring:
- PostgreSQL Crosstab Query
Den speciella "svårigheten" här:inget egentligt "värde". Så lägg till 1 AS val
som sista kolumn.
Okänt antal kategorier
En helt dynamisk fråga (med okänd resultattyp) är inte möjlig i en enda fråga. Du behöver två frågor. Bygg först en sats som ovan dynamiskt och kör den sedan. Detaljer:
-
Välja flera max()-värden med en enda SQL-sats
-
PostgreSQL konvertera kolumner till rader? Transponera?
-
Generera dynamiskt kolumner för korstabell i PostgreSQL
-
Dynamiskt alternativ att pivotera med CASE och GROUP BY
För många kategorier
Om du överskrider det maximala antalet kolumner (1600) är en klassisk korstabell omöjlig, eftersom resultatet inte kan representeras med enskilda kolumner. (Dessutom skulle mänskliga ögon knappast kunna läsa en tabell med så många kolumner)
Matriser eller dokumenttyper som hstore
eller jsonb
är alternativet. Här är en lösning med arrayer:
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;
-
Bygg hela rutnätet av värden med:
(SELECT DISTINCT colb FROM matrix) b CROSS JOIN (SELECT DISTINCT cola FROM matrix) a
-
LEFT JOIN
befintliga kombinationer, ordna efter den numeriska delen av namnet och aggregera till arrayer.right(colb, -1)::int
beskär det inledande tecknet från 'A3' och siffrorna till heltal så att vi får en korrekt sorteringsordning.
Grundmatris
Om du bara vill ha en tabell med 0
en 1
där x = y
, detta kan fås billigare:
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 angav i kommentarerna.
Observera att sqlfiddle.com för närvarande har en bugg som dödar visningen av arrayvärden. Så jag castade till text
där för att komma runt det.