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

PostgreSQL 9.3:tabella pivot dinamica

Puoi farlo con crosstab() dal modulo aggiuntivo tablefunc:

SELECT b
     , COALESCE(a1, 0) AS "A1"
     , COALESCE(a2, 0) AS "A2"
     , COALESCE(a3, 0) AS "A3"
     , ... -- all the way up to "A30"
FROM   crosstab(
         'SELECT colb, cola, 1 AS val FROM matrix
          ORDER  BY 1,2'
        , $$SELECT 'A'::text || g FROM generate_series(1,30) g$$
       ) AS t (b text
             , a1  int, a2  int, a3  int, a4  int, a5  int, a6  int
             , a7  int, a8  int, a9  int, a10 int, a11 int, a12 int
             , a13 int, a14 int, a15 int, a16 int, a17 int, a18 int
             , a19 int, a20 int, a21 int, a22 int, a23 int, a24 int
             , a25 int, a26 int, a27 int, a28 int, a29 int, a30 int);

Se NULL invece di 0 funziona anche, può essere solo SELECT * nella query esterna.
Spiegazione dettagliata:

  • Query a campi incrociati PostgreSQL

La "difficoltà" speciale qui:nessun "valore" effettivo. Quindi aggiungi 1 AS val come ultima colonna.

Numero sconosciuto di categorie

Una query completamente dinamica (con tipo di risultato sconosciuto) non è possibile in una singola query. Ne servono due interrogazioni. Per prima cosa costruisci un'istruzione come quella sopra in modo dinamico, quindi eseguila. Dettagli:

  • Selezione di più valori max() utilizzando una singola istruzione SQL

  • PostgreSQL converte le colonne in righe? Trasporre?

  • Genera dinamicamente colonne per campi incrociati in PostgreSQL

  • Alternativa dinamica al pivot con CASE e GROUP BY

Troppe categorie

Se si supera il numero massimo di colonne (1600), un classico campo incrociato è impossibile, perché il risultato non può essere rappresentato con singole colonne. (Inoltre, gli occhi umani difficilmente sarebbero in grado di leggere una tabella con così tante colonne)

Array o tipi di documenti come hstore o jsonb sono l'alternativa. Ecco una soluzione con gli array:

SELECT colb, array_agg(cola) AS colas
FROM  (
   SELECT colb, right(colb, -1)::int AS sortb
        , CASE WHEN m.cola IS NULL THEN 0 ELSE 1 END AS cola
   FROM        (SELECT DISTINCT colb FROM matrix) b
   CROSS  JOIN (SELECT DISTINCT cola FROM matrix) a
   LEFT   JOIN matrix m USING (colb, cola)
   ORDER  BY sortb, right(cola, -1)::int 
   ) sub
GROUP  BY 1, sortb
ORDER  BY sortb;
  • Costruisci la griglia completa dei valori con:

                (SELECT DISTINCT colb FROM matrix) b
    CROSS  JOIN (SELECT DISTINCT cola FROM matrix) a
    
  • LEFT JOIN combinazioni esistenti, ordinate in base alla parte numerica del nome e aggregate in array.

    • right(colb, -1)::int taglia il carattere principale da "A3" e trasforma le cifre in numeri interi in modo da ottenere un ordinamento corretto.

Matrice di base

Se vuoi solo una tabella di 0 un 1 dove x = y , questo può essere più conveniente:

SELECT x, array_agg((x = y)::int) AS y_arr
FROM   generate_series(1,10) x
     , generate_series(1,10) y
GROUP  BY 1
ORDER  BY 1;

SQL Fiddle basandosi su quello che hai fornito nei commenti.

Nota che sqlfiddle.com ha attualmente un bug che uccide la visualizzazione dei valori dell'array. Quindi trasmetto a text lì per aggirarlo.