PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

PostgreSQL 9.3:Dynamische Pivot-Tabelle

Sie können dies mit crosstab() tun aus dem Zusatzmodul 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);

Wenn NULL statt 0 funktioniert auch, es kann einfach SELECT * sein in der äußeren Abfrage.
Detaillierte Erklärung:

  • PostgreSQL-Kreuztabellenabfrage

Die besondere "Schwierigkeit" hier:kein eigentlicher "Wert". Fügen Sie also 1 AS val hinzu als letzte Spalte.

Unbekannte Anzahl an Kategorien

Eine vollständig dynamische Abfrage (mit unbekanntem Ergebnistyp) ist in einer einzelnen Abfrage nicht möglich. Du brauchst zwei Abfragen. Erstellen Sie zunächst dynamisch eine Anweisung wie die obige und führen Sie sie dann aus. Einzelheiten:

  • Auswählen mehrerer max()-Werte mit einer einzigen SQL-Anweisung

  • PostgreSQL Spalten in Zeilen konvertieren? Transponieren?

  • Generieren Sie dynamisch Spalten für Kreuztabellen in PostgreSQL

  • Dynamische Alternative zum Pivot mit CASE und GROUP BY

Zu viele Kategorien

Wenn Sie die maximale Spaltenanzahl (1600) überschreiten, ist eine klassische Kreuztabelle nicht möglich, da das Ergebnis nicht mit einzelnen Spalten dargestellt werden kann. (Außerdem wäre das menschliche Auge kaum in der Lage, eine Tabelle mit so vielen Spalten zu lesen)

Arrays oder Dokumenttypen wie hstore oder jsonb sind die Alternative. Hier ist eine Lösung mit 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;
  • Erstellen Sie das vollständige Werteraster mit:

                (SELECT DISTINCT colb FROM matrix) b
    CROSS  JOIN (SELECT DISTINCT cola FROM matrix) a
    
  • LEFT JOIN vorhandene Kombinationen, sortieren Sie sie nach dem numerischen Teil des Namens und aggregieren Sie sie in Arrays.

    • right(colb, -1)::int schneidet das führende Zeichen von 'A3' ab und wandelt die Ziffern in Ganzzahlen um, damit wir eine korrekte Sortierreihenfolge erhalten.

Basismatrix

Wenn Sie nur eine Tabelle von 0 wollen eine 1 wobei x = y , das ist günstiger zu haben:

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-Geige aufbauend auf dem, das Sie in den Kommentaren angegeben haben.

Beachten Sie, dass sqlfiddle.com derzeit einen Fehler hat, der die Anzeige von Array-Werten beendet. Also wandle ich in text um da, um es zu umgehen.