Installer det ekstra modul tablefunc
en gang per database, som giver funktionen crosstab()
. Siden Postgres 9.1 kan du bruge CREATE EXTENSION
for det:
CREATE EXTENSION IF NOT EXISTS tablefunc;
Forbedret testcase
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
Simpel form - ikke egnet til manglende attributter
crosstab(text)
med 1 input parameter:
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);
Returnerer:
Section | Active | Inactive ---------+--------+---------- A | 1 | 2 B | 4 | 5 C | 7 | -- !!
- Intet behov for at caste og omdøbe.
- Bemærk det forkerte resultat for
C
:værdien7
er udfyldt for første kolonne. Nogle gange er denne adfærd ønskværdig, men ikke til denne brug. - Den simple form er også begrænset til præcis tre kolonner i den angivne inputforespørgsel:rækkenavn , kategori , værdi . Der er ikke plads til ekstra kolonner som i alternativet med 2 parametre nedenfor.
Sikker form
crosstab(text, text)
med 2 inputparametre:
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);
Returnerer:
Section | Active | Inactive ---------+--------+---------- A | 1 | 2 B | 4 | 5 C | | 7 -- !!
-
Bemærk det korrekte resultat for
C
. -
Den anden parameter kan være enhver forespørgsel, der returnerer én række attribut, der matcher rækkefølgen af kolonnedefinitionen i slutningen. Ofte vil du gerne forespørge om forskellige attributter fra den underliggende tabel på denne måde:
'SELECT DISTINCT attribute FROM tbl ORDER BY 1'
Det står i manualen.
Da du alligevel skal stave alle kolonner i en kolonnedefinitionsliste (undtagen foruddefinerede crosstabN()
varianter), er det typisk mere effektivt at give en kort liste i en VALUES
udtryk som vist:
$$VALUES ('Active'::text), ('Inactive')$$)
Eller (ikke i manualen):
$$SELECT unnest('{Active,Inactive}'::text[])$$ -- short syntax for long lists
-
Jeg brugte dollarnotering for at gøre det lettere at citere.
-
Du kan endda udskrive kolonner med forskellige datatyper med
crosstab(text, text)
- så længe tekstrepræsentationen af værdikolonnen er gyldig input for måltypen. På denne måde kan du have attributter af forskellig art og outputtext
,date
,numeric
osv. for respektive attributter. Der er et kodeeksempel i slutningen af kapitletcrosstab(text, text)
i manualen.
db<>spil her
Effekt af overskydende inputrækker
Overskydende inputrækker håndteres forskelligt - dublerede rækker for den samme ("row_name", "category") kombination - (section, status)
i ovenstående eksempel.
1-parameteren formularen udfylder tilgængelige værdikolonner fra venstre mod højre. Overskydende værdier kasseres.
Tidligere inputrækker vinder.
2-parameteren formular tildeler hver inputværdi til dens dedikerede kolonne og overskriver enhver tidligere tildeling.
Senere inputrækker vinder.
Typisk har du ikke dubletter til at begynde med. Men hvis du gør det, skal du omhyggeligt justere sorteringsrækkefølgen til dine krav - og dokumentere, hvad der sker.
Eller få hurtige vilkårlige resultater, hvis du er ligeglad. Bare vær opmærksom på effekten.
Avancerede eksempler
-
Pivot på flere kolonner ved hjælp af Tablefunc - demonstrerer også nævnte "ekstra kolonner"
-
Dynamisk alternativ til pivotering med CASE og GROUP BY
\crosstabview
i psql
Postgres 9.6 tilføjede denne metakommando til dens interaktive standardterminal psql. Du kan køre den forespørgsel, du ville bruge som første crosstab()
parameter og feed den til \crosstabview
(umiddelbart eller i næste trin). Ligesom:
db=> SELECT section, status, ct FROM tbl \crosstabview
Tilsvarende resultat som ovenfor, men det er en repræsentationsfunktion på klientsiden udelukkende. Input rækker behandles lidt anderledes, derfor ORDER BY
er ikke påkrævet. Detaljer for \crosstabview
i manualen. Der er flere kodeeksempler nederst på siden.
Relateret svar på dba.SE af Daniel Vérité (forfatteren af psql-funktionen):
- Hvordan genererer jeg en pivoteret CROSS JOIN, hvor den resulterende tabeldefinition er ukendt?