sql >> Databasteknik >  >> RDS >> PostgreSQL

PostgreSQL 9.3:Dynamisk pivottabell

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.




  1. OPENJSON "Felaktig syntax nära nyckelordet 'med'." i SQL Server (LÖST)

  2. WordPress – Bakom kulisserna, del 1

  3. Hur uppdaterar man alla kolumner med INSERT ... ON CONFLICT ...?

  4. Drivrutinen kunde inte upprätta en säker anslutning till SQL Server genom att använda Secure Sockets Layer (SSL)-kryptering