sql >> Databasteknik >  >> RDS >> PostgreSQL

PostgreSQL Crosstab Query

Installera tilläggsmodulen tablefunc en gång per databas, som tillhandahåller funktionen crosstab() . Sedan Postgres 9.1 kan du använda CREATE EXTENSION för det:

CREATE EXTENSION IF NOT EXISTS tablefunc;

Förbättrat 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

Enkel form – inte lämplig för saknade attribut

crosstab(text) med 1 ingångsparameter:

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);

Returnerar:

 Section | Active | Inactive
---------+--------+----------
 A       |      1 |        2
 B       |      4 |        5
 C       |      7 |           -- !!
  • Inget behov av att casta och byta namn.
  • Observera det felaktiga resultat för C :värdet 7 fylls i för den första kolumnen. Ibland är detta beteende önskvärt, men inte för detta användningsfall.
  • Den enkla formen är också begränsad till exakt tre kolumner i den angivna inmatningsfrågan:radnamn , kategori , värde . Det finns inget utrymme för extra kolumner som i alternativet med två parametrar nedan.

Säker form

crosstab(text, text) med 2 ingångsparametrar:

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);

Returnerar:

 Section | Active | Inactive
---------+--------+----------
 A       |      1 |        2
 B       |      4 |        5
 C       |        |        7  -- !!
  • Notera det korrekta resultatet för C .

  • Den andra parametern kan vara vilken fråga som helst som returnerar en rad per attribut som matchar ordningen för kolumndefinitionen i slutet. Ofta vill du fråga efter distinkta attribut från den underliggande tabellen så här:

      'SELECT DISTINCT attribute FROM tbl ORDER BY 1'
    

Det står i manualen.

Eftersom du måste stava alla kolumner i en kolumndefinitionslista ändå (förutom fördefinierade crosstabN() varianter), är det vanligtvis mer effektivt att tillhandahålla en kort lista i en VALUES uttryck som visas:

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

Eller (inte i manualen):

    $$SELECT unnest('{Active,Inactive}'::text[])$$  -- short syntax for long lists
  • Jag använde dollarnotering för att göra det enklare att citera.

  • Du kan till och med skriva ut kolumner med olika datatyper med crosstab(text, text) - så länge som textrepresentationen av värdekolumnen är giltig inmatning för måltypen. På så sätt kan du ha attribut av olika slag och utdata text , date , numeric etc. för respektive attribut. Det finns ett kodexempel i slutet av kapitlet crosstab(text, text) i manualen.

db<>spela här

Effekt av överflödiga inmatningsrader

Överflödiga inmatningsrader hanteras på olika sätt - dubbletter av rader för samma ("radnamn", "kategori") kombination - (section, status) i exemplet ovan.

1-parametern formuläret fyller i tillgängliga värdekolumner från vänster till höger. Överskjutande värden kasseras.
Tidigare inmatningsrader vinner.

2-parametern form tilldelar varje inmatningsvärde till dess dedikerade kolumn och skriver över alla tidigare tilldelningar.
Senare inmatningsrader vinner.

Vanligtvis har du inga dubbletter till att börja med. Men om du gör det, justera noggrant sorteringsordningen efter dina krav - och dokumentera vad som händer.
Eller få snabba godtyckliga resultat om du inte bryr dig. Var bara medveten om effekten.

Avancerade exempel

  • Pivotera på flera kolumner med hjälp av Tablefunc - demonstrerar också nämnda "extra kolumner"

  • Dynamiskt alternativ att pivotera med CASE och GROUP BY


\crosstabview i psql

Postgres 9.6 lade till detta metakommando till sin interaktiva standardterminal psql. Du kan köra frågan du skulle använda som första crosstab() parametern och mata den till \crosstabview (omedelbart eller i nästa steg). Gilla:

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

Liknande resultat som ovan, men det är en representationsfunktion på klientsidan uteslutande. Inmatningsrader behandlas något annorlunda, därför ORDER BY behövs inte. Detaljer för \crosstabview i manualen. Det finns fler kodexempel längst ner på den sidan.

Relaterat svar på dba.SE av Daniel Vérité (författaren till psql-funktionen):

  • Hur skapar jag en pivoterad CROSS JOIN där den resulterande tabelldefinitionen är okänd?


  1. Bevara förälder-barn-relationer när du kopierar hierarkisk data

  2. MySQL antal objekt inom i klausul

  3. MySql-fel:Kan inte uppdatera tabellen i lagrad funktion/trigger eftersom den redan används av en sats som anropade denna lagrade funktion/trigger

  4. Är främmande nycklar verkligen nödvändiga i en databasdesign?