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

PostgreSQL-Kreuztabellenabfrage

Installieren Sie das Zusatzmodul tablefunc einmal pro Datenbank, die die Funktion crosstab() bereitstellt . Seit Postgres 9.1 können Sie CREATE EXTENSION verwenden dafür:

CREATE EXTENSION IF NOT EXISTS tablefunc;

Verbesserter Testfall

CREATE TABLE tbl (
   section   text
 , status    text
 , ct        integer  -- "count" is a reserved word in standard SQL
);

INSERT INTO tbl VALUES 
  ('A', 'Active', 1), ('A', 'Inactive', 2)
, ('B', 'Active', 4), ('B', 'Inactive', 5)
                    , ('C', 'Inactive', 7);  -- ('C', 'Active') is missing

Einfache Form - nicht geeignet für fehlende Attribute

crosstab(text) mit 1 Eingabeparameter:

SELECT *
FROM   crosstab(
   'SELECT section, status, ct
    FROM   tbl
    ORDER  BY 1,2'  -- needs to be "ORDER BY 1,2" here
   ) AS ct ("Section" text, "Active" int, "Inactive" int);

Rückgabe:

 Section | Active | Inactive
---------+--------+----------
 A       |      1 |        2
 B       |      4 |        5
 C       |      7 |           -- !!
  • Kein Casting und Umbenennen erforderlich.
  • Beachten Sie die Falschheit Ergebnis für C :der Wert 7 wird für die erste Spalte ausgefüllt. Manchmal ist dieses Verhalten wünschenswert, aber nicht für diesen Anwendungsfall.
  • Die einfache Form ist auch auf genau beschränkt drei Spalten in der bereitgestellten Eingabeabfrage:row_name , Kategorie , Wert . Es ist kein Platz für zusätzliche Spalten wie in der 2-Parameter-Alternative unten.

Sichere Form

crosstab(text, text) mit 2 Eingabeparameter:

SELECT *
FROM   crosstab(
   'SELECT section, status, ct
    FROM   tbl
    ORDER  BY 1,2'  -- could also just be "ORDER BY 1" here

  , $$VALUES ('Active'::text), ('Inactive')$$
   ) AS ct ("Section" text, "Active" int, "Inactive" int);

Rückgabe:

 Section | Active | Inactive
---------+--------+----------
 A       |      1 |        2
 B       |      4 |        5
 C       |        |        7  -- !!
  • Beachten Sie das korrekte Ergebnis für C .

  • Der zweite Parameter kann jede Abfrage sein, die eine Zeile zurückgibt pro Attribut entsprechend der Reihenfolge der Spaltendefinition am Ende. Häufig möchten Sie bestimmte Attribute aus der zugrunde liegenden Tabelle wie folgt abfragen:

      'SELECT DISTINCT attribute FROM tbl ORDER BY 1'
    

Das steht im Handbuch.

Da Sie in einer Spaltendefinitionsliste ohnehin alle Spalten buchstabieren müssen (außer vordefinierten crosstabN() Varianten), ist es in der Regel effizienter, eine kurze Liste in einem VALUES bereitzustellen Ausdruck wie demonstriert:

    $$VALUES ('Active'::text), ('Inactive')$$)

Oder (nicht im Handbuch):

    $$SELECT unnest('{Active,Inactive}'::text[])$$  -- short syntax for long lists
  • Ich habe Dollar-Notierung verwendet um das Zitieren zu erleichtern.

  • Sie können sogar Spalten mit anders ausgeben Datentypen mit crosstab(text, text) - solange die Textdarstellung der Wertespalte eine gültige Eingabe für den Zieltyp ist. Auf diese Weise können Sie Attribute unterschiedlicher Art haben und text ausgeben , date , numeric usw. für entsprechende Attribute. Ein Codebeispiel befindet sich am Ende des Kapitels crosstab(text, text) im Handbuch.

db<>hier fummeln

Auswirkung von überschüssigen Eingabezeilen

Überschüssige Eingabezeilen werden anders behandelt - doppelte Zeilen für die gleiche ("row_name", "category") Kombination - (section, status) im obigen Beispiel.

Der 1-Parameter Das Formular füllt verfügbare Wertspalten von links nach rechts aus. Überschüssige Werte werden verworfen.
Frühere Eingabezeilen gewinnen.

Der 2-Parameter Form weist jeden Eingabewert seiner dedizierten Spalte zu und überschreibt alle vorherigen Zuweisungen.
Spätere Eingabezeilen gewinnen.

Normalerweise haben Sie zunächst keine Duplikate. Aber wenn doch, passen Sie die Sortierreihenfolge sorgfältig an Ihre Anforderungen an - und dokumentieren Sie, was passiert.
Oder erhalten Sie schnell willkürliche Ergebnisse, wenn es Ihnen egal ist. Seien Sie sich nur der Wirkung bewusst.

Erweiterte Beispiele

  • Pivot auf mehrere Spalten mit Tablefunc – demonstriert auch die erwähnten „zusätzlichen Spalten“

  • Dynamische Alternative zum Pivot mit CASE und GROUP BY


\crosstabview in psql

Postgres 9.6 hat diesen Metabefehl zu seinem standardmäßigen interaktiven Terminal psql hinzugefügt. Sie können die Abfrage ausführen, die Sie als erste crosstab() verwenden würden Parameter und füttern Sie ihn an \crosstabview (sofort oder im nächsten Schritt). Wie:

db=> SELECT section, status, ct FROM tbl \crosstabview

Ähnliches Ergebnis wie oben, aber es ist eine Repräsentationsfunktion auf der Client-Seite ausschließlich. Eingabezeilen werden etwas anders behandelt, daher ORDER BY ist nicht nötig. Details für \crosstabview im Handbuch. Unten auf dieser Seite finden Sie weitere Codebeispiele.

Zugehörige Antwort auf dba.SE von Daniel Vérité (dem Autor des psql-Features):

  • Wie erzeuge ich einen pivotierten CROSS JOIN, bei dem die resultierende Tabellendefinition unbekannt ist?