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 Wert7
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 undtext
ausgeben ,date
,numeric
usw. für entsprechende Attribute. Ein Codebeispiel befindet sich am Ende des Kapitelscrosstab(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?