sql >> Database >  >> RDS >> PostgreSQL

PostgreSQL-kruistabelquery

Installeer de extra module tablefunc eenmaal per database, die de functie crosstab() . biedt . Sinds Postgres 9.1 kunt u CREATE EXTENSION . gebruiken daarvoor:

CREATE EXTENSION IF NOT EXISTS tablefunc;

Verbeterde 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

Eenvoudige vorm - niet geschikt voor ontbrekende kenmerken

crosstab(text) met 1 invoerparameter:

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

Retourneren:

 Section | Active | Inactive
---------+--------+----------
 A       |      1 |        2
 B       |      4 |        5
 C       |      7 |           -- !!
  • Casten en hernoemen is niet nodig.
  • Let op de onjuiste resultaat voor C :de waarde 7 is ingevuld voor de eerste kolom. Soms is dit gedrag wenselijk, maar niet voor dit gebruik.
  • De eenvoudige vorm is ook beperkt tot exact drie kolommen in de opgegeven invoerquery:row_name , categorie , waarde . Er is geen ruimte voor extra kolommen zoals in het 2-parameter alternatief hieronder.

Veilig formulier

crosstab(text, text) met 2 invoerparameters:

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

Retourneren:

 Section | Active | Inactive
---------+--------+----------
 A       |      1 |        2
 B       |      4 |        5
 C       |        |        7  -- !!
  • Let op het juiste resultaat voor C .

  • De tweede parameter kan elke zoekopdracht zijn die één rij retourneert per attribuut overeenkomend met de volgorde van de kolomdefinitie aan het einde. Vaak wilt u verschillende kenmerken van de onderliggende tabel als volgt opvragen:

      'SELECT DISTINCT attribute FROM tbl ORDER BY 1'
    

Dat staat in de handleiding.

Aangezien u toch alle kolommen in een kolomdefinitielijst moet spellen (behalve voor vooraf gedefinieerde crosstabN() varianten), is het doorgaans efficiënter om een ​​korte lijst te geven in een VALUES uitdrukking zoals gedemonstreerd:

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

Of (niet in de handleiding):

    $$SELECT unnest('{Active,Inactive}'::text[])$$  -- short syntax for long lists
  • Ik gebruikte dollarcitaten om citeren gemakkelijker te maken.

  • U kunt zelfs kolommen uitvoeren met verschillende gegevenstypen met crosstab(text, text) - zolang de tekstweergave van de waardekolom geldige invoer is voor het doeltype. Op deze manier kunt u attributen van verschillende aard hebben en text uitvoeren , date , numeric enz. voor respectieve attributen. Er is een codevoorbeeld aan het einde van het hoofdstuk crosstab(text, text) in de handleiding.

db<>viool hier

Effect van overtollige invoerrijen

Overtollige invoerrijen worden anders behandeld - dubbele rijen voor dezelfde ("rijnaam", "categorie") combinatie - (section, status) in het bovenstaande voorbeeld.

De 1-parameter formulier vult beschikbare waardekolommen van links naar rechts in. Overtollige waarden worden weggegooid.
Vroeger ingevoerde rijen winnen.

De 2-parameter formulier wijst elke invoerwaarde toe aan de daarvoor bestemde kolom, waarbij eerdere toewijzingen worden overschreven.
Latere invoerrijen winnen.

Normaal gesproken heb je om te beginnen geen duplicaten. Maar als je dat doet, pas dan de sorteervolgorde zorgvuldig aan je eisen aan - en documenteer wat er gebeurt.
Of krijg snel willekeurige resultaten als het je niet kan schelen. Wees je bewust van het effect.

Geavanceerde voorbeelden

  • Draaien op meerdere kolommen met Tablefunc - demonstreert ook de genoemde "extra kolommen"

  • Dynamisch alternatief voor draaien met CASE en GROUP BY


\crosstabview in psql

Postgres 9,6 heeft deze meta-opdracht toegevoegd aan de standaard interactieve terminal psql. U kunt de query uitvoeren die u zou gebruiken als eerste crosstab() parameter en voer het naar \crosstabview (direct of in de volgende stap). Vind ik leuk:

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

Vergelijkbaar resultaat als hierboven, maar het is een weergavefunctie aan de clientzijde uitsluitend. Invoerrijen worden iets anders behandeld, vandaar ORDER BY is niet nodig. Details voor \crosstabview in de handleiding. Er zijn meer codevoorbeelden onderaan die pagina.

Gerelateerd antwoord op dba.SE door Daniel Vérité (de auteur van de psql-functie):

  • Hoe genereer ik een gedraaide CROSS JOIN waarvan de resulterende tabeldefinitie onbekend is?


  1. InnoDB dwingen buitenlandse sleutels op een tafel/tabellen opnieuw te controleren?

  2. Kan geen databasetabel met de naam 'gebruiker' maken in PostgreSQL

  3. Oracle CREATE TABLE Commando in PL/SQL met 10 voorbeelden

  4. Verschil tussen sys.sql_modules, sys.system_sql_modules en sys.all_sql_modules in SQL Server