PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

Query a campi incrociati PostgreSQL

Installa il modulo aggiuntivo tablefunc una volta per database, che fornisce la funzione crosstab() . Da Postgres 9.1 puoi usare CREATE EXTENSION per quello:

CREATE EXTENSION IF NOT EXISTS tablefunc;

Campo di prova migliorato

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

Modulo semplice - non adatto per attributi mancanti

crosstab(text) con 1 parametro di input:

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

Resi:

Sezione
 Section | Active | Inactive
---------+--------+----------
 A       |      1 |        2
 B       |      4 |        5
 C       |      7 |           -- !!
  • Non c'è bisogno di casting e ridenominazione.
  • Nota l'errata risultato per C :il valore 7 viene compilato per la prima colonna. A volte, questo comportamento è desiderabile, ma non per questo caso d'uso.
  • Anche il modulo semplice è limitato a esattamente tre colonne nella query di input fornita:nome_riga , categoria , valore . Non c'è spazio per colonne extra come nell'alternativa a 2 parametri di seguito.

Modulo sicuro

crosstab(text, text) con 2 parametri di input:

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

Resi:

Sezione
 Section | Active | Inactive
---------+--------+----------
 A       |      1 |        2
 B       |      4 |        5
 C       |        |        7  -- !!
  • Nota il risultato corretto per C .

  • Il secondo parametro può essere qualsiasi query che restituisce una riga per attributo che corrisponde all'ordine della definizione di colonna alla fine. Spesso vorrai interrogare attributi distinti dalla tabella sottostante in questo modo:

      'SELECT DISTINCT attribute FROM tbl ORDER BY 1'
    

È nel manuale.

Dal momento che devi comunque precisare tutte le colonne in un elenco di definizioni di colonna (tranne per crosstabN() varianti), in genere è più efficiente fornire un breve elenco in un VALUES espressione come dimostrato:

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

Oppure (non nel manuale):

    $$SELECT unnest('{Active,Inactive}'::text[])$$  -- short syntax for long lists
  • Ho usato citando il dollaro per semplificare la quotazione.

  • Puoi anche generare colonne con diverse tipi di dati con crosstab(text, text) - fintanto che la rappresentazione testuale della colonna del valore è un input valido per il tipo di destinazione. In questo modo potresti avere attributi di tipo diverso e produrre text , date , numeric ecc. per i rispettivi attributi. C'è un esempio di codice alla fine del capitolo crosstab(text, text) nel manuale.

db<>gioca qui

Effetto delle righe di input in eccesso

Le righe di input in eccesso vengono gestite in modo diverso:righe duplicate per la stessa combinazione ("nome_riga", "categoria") - (section, status) nell'esempio sopra.

Il parametro 1 il modulo riempie le colonne dei valori disponibili da sinistra a destra. I valori in eccesso vengono eliminati.
Le righe di input precedenti vincono.

Il 2-parametro form assegna ogni valore di input alla sua colonna dedicata, sovrascrivendo qualsiasi assegnazione precedente.
Le righe di input successive vincono.

In genere, non hai duplicati per cominciare. Ma se lo fai, adatta con attenzione l'ordinamento in base alle tue esigenze e documenta ciò che sta accadendo.
Oppure ottieni risultati arbitrari rapidi se non ti interessa. Basta essere consapevoli dell'effetto.

Esempi avanzati

  • Ruota su più colonne utilizzando Tablefunc, dimostrando anche le "colonne extra" citate

  • Alternativa dinamica al pivot con CASE e GROUP BY


\crosstabview in psql

Postgres 9.6 aggiunto questo meta-comando al suo terminale interattivo predefinito psql. Puoi eseguire la query che useresti come prima crosstab() parametro e invialo a \crosstabview (immediatamente o nel passaggio successivo). Come:

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

Risultato simile al precedente, ma è una funzione di rappresentazione lato client esclusivamente. Le righe di input sono trattate in modo leggermente diverso, quindi ORDER BY non è richiesto. Dettagli per \crosstabview nel manuale. Ci sono altri esempi di codice in fondo a quella pagina.

Risposta correlata su dba.SE di Daniel Vérité (l'autore della funzione psql):

  • Come faccio a generare un CROSS JOIN con pivot in cui la definizione della tabella risultante è sconosciuta?