Domanda

Qualcuno sa come creare query a campi incrociati in PostgreSQL?
Ad esempio ho la seguente tabella:

Section    Status    Count
A          Active    1
A          Inactive  2
B          Active    4
B          Inactive  5

Vorrei che la query restituisse la seguente tabella incrociata:

Section    Active    Inactive
A          1         2
B          4         5

È possibile?

È stato utile?

Soluzione

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

CREATE EXTENSION IF NOT EXISTS tablefunc;

Caso di test 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

Forma semplice: non adatta per gli 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);

Ritorna:

 Section | Active | Inactive
---------+--------+----------
 A       |      1 |        2
 B       |      4 |        5
 C       |      7 |           -- !!
  • Non è necessario trasmettere e rinominare.
  • Notare la errato risultato per C:il valore 7 è compilato per la prima colonna.A volte questo comportamento è auspicabile, ma non per questo caso d'uso.
  • Anche la forma semplice è limitata a esattamente tre colonne nella query di input fornita: nome_riga, categoria, valore.Non c'è spazio per colonne aggiuntive come nell'alternativa a 2 parametri di seguito.

Forma sicura

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 ('Attivo'::testo), ('Non attivo')$$
   ) AS ct ("Section" text, "Active" int, "Inactive" int);

Ritorna:

 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 ne restituisce uno riga per attributo corrispondente 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'
    

    Questo è nel manuale.

    Poiché è comunque necessario precisare tutte le colonne in un elenco di definizioni di colonne (ad eccezione di predefinite crosstabN() varianti), in genere è più efficiente fornire un breve elenco in a VALUES espressione come dimostrato:

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

    Oppure (non nel manuale):

    $$SELECT unnest('{Active,Inactive}'::text[])$$  -- short syntax for long lists
    
  • ero solito quotazione del dollaro per rendere più semplice la citazione.

  • Puoi anche generare colonne con diverso tipi di dati con crosstab(text, text) - purché la rappresentazione testuale della colonna valore sia un input valido per il tipo di destinazione.In questo modo potresti avere attributi di diverso tipo e output text, date, numeric eccetera.per i rispettivi attributi.Alla fine del file è presente un esempio di codice capitolo crosstab(text, text) nel manuale.

db<>violino Qui

Esempi avanzati


\crosstabview inpsql

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

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

Risultato simile a quello sopra, ma è a funzionalità di rappresentazione lato client esclusivamente.Pertanto, le righe di input vengono trattate in modo leggermente diverso 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):



IL risposta precedentemente accettata è obsoleto.

  • La variante della funzione crosstab(text, integer) è obsoleto.Il secondo integer il parametro viene ignorato.Cito il attuale Manuale:

    crosstab(text sql, int N) ...

    Versione obsoleta di crosstab(text).Il parametro N viene ora ignorato, poiché il numero di colonne di valore è sempre determinato dalla query chiamante

  • Casting e ridenominazione inutili.

  • Fallisce se una riga non ha tutti gli attributi.Vedi la variante sicura con due parametri di input sopra per gestire correttamente gli attributi mancanti.

  • ORDER BY è richiesto nel formato a un parametro di crosstab(). Il manuale:

    In pratica la query SQL dovrebbe sempre specificare ORDER BY 1,2 per garantire che le righe di input siano correttamente ordinate

Altri suggerimenti

Puoi usare il crosstab() funzione del modulo aggiuntivo tablefunz - che devi installare una volta per banca dati.A partire da PostgreSQL 9.1 puoi usare CREATE EXTENSION per quello:

CREATE EXTENSION tablefunc;

Nel tuo caso, credo che sarebbe simile a questo:

CREATE TABLE t (Section CHAR(1), Status VARCHAR(10), Count integer);

INSERT INTO t VALUES ('A', 'Active',   1);
INSERT INTO t VALUES ('A', 'Inactive', 2);
INSERT INTO t VALUES ('B', 'Active',   4);
INSERT INTO t VALUES ('B', 'Inactive', 5);

SELECT row_name AS Section,
       category_1::integer AS Active,
       category_2::integer AS Inactive
FROM crosstab('select section::text, status, count::text from t',2)
            AS ct (row_name text, category_1 text, category_2 text);
SELECT section,
       SUM(CASE status WHEN 'Active' THEN count ELSE 0 END) AS active, --here you pivot each status value as a separate column explicitly
       SUM(CASE status WHEN 'Inactive' THEN count ELSE 0 END) AS inactive --here you pivot each status  value as a separate column explicitly

FROM t
GROUP BY section

Soluzione con JSON di aggregazione:

CREATE TEMP TABLE t (
  section   text
, status    text
, ct        integer  -- don't use "count" as column name.
);

INSERT INTO t VALUES 
  ('A', 'Active', 1), ('A', 'Inactive', 2)
, ('B', 'Active', 4), ('B', 'Inactive', 5)
                   , ('C', 'Inactive', 7); 


SELECT section,
       (obj ->> 'Active')::int AS active,
       (obj ->> 'Inactive')::int AS inactive
FROM (SELECT section, json_object_agg(status,ct) AS obj
      FROM t
      GROUP BY section
     )X

Spiacente, questo non è completa perché non posso provarlo qui, ma può farti fuori nella giusta direzione. Sto traducendo dall'uso ho qualcosa che rende una query simile:

select mt.section, mt1.count as Active, mt2.count as Inactive
from mytable mt
left join (select section, count from mytable where status='Active')mt1
on mt.section = mt1.section
left join (select section, count from mytable where status='Inactive')mt2
on mt.section = mt2.section
group by mt.section,
         mt1.count,
         mt2.count
order by mt.section asc;

Il codice su cui sto lavorando da è:

select m.typeID, m1.highBid, m2.lowAsk, m1.highBid - m2.lowAsk as diff, 100*(m1.highBid - m2.lowAsk)/m2.lowAsk as diffPercent
from mktTrades m
   left join (select typeID,MAX(price) as highBid from mktTrades where bid=1 group by typeID)m1
   on m.typeID = m1.typeID
   left join (select typeID,MIN(price) as lowAsk  from mktTrades where bid=0 group by typeID)m2
   on m1.typeID = m2.typeID
group by m.typeID, 
         m1.highBid, 
         m2.lowAsk
order by diffPercent desc;

che restituirà un typeid, l'offerta prezzo più alto e il prezzo più basso ha chiesto e la differenza tra i due (una differenza positiva significherebbe qualcosa potrebbe essere acquistato per meno di quanto può essere venduto).

funzione Crosstab è disponibile sotto l'estensione tablefunc. Dovrete creare questa proroga di un tempo per il database.

CREATE ESTENSIONE tablefunc;

È possibile utilizzare il sottostante codice per creare tabelle pivot utilizzando la scheda trasversale:

create table test_Crosstab( section text,
<br/>status text,
<br/>count numeric)

<br/>insert into test_Crosstab values ( 'A','Active',1)
                <br/>,( 'A','Inactive',2)
                <br/>,( 'B','Active',4)
                <br/>,( 'B','Inactive',5)

select * from crosstab(
<br/>'select section
    <br/>,status
    <br/>,count
    <br/>from test_crosstab'
    <br/>)as ctab ("Section" text,"Active" numeric,"Inactive" numeric)
Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top