Mysql
 sql >> Database >  >> RDS >> Mysql

MySQL:da righe a colonne

Aggiungerò una spiegazione un po' più lunga e dettagliata dei passaggi da eseguire per risolvere questo problema. Mi scuso se è troppo lungo.

Inizierò con la base che hai fornito e la userò per definire un paio di termini che userò per il resto di questo post. Questa sarà la tabella di base :

select * from history;

+--------+----------+-----------+
| hostid | itemname | itemvalue |
+--------+----------+-----------+
|      1 | A        |        10 |
|      1 | B        |         3 |
|      2 | A        |         9 |
|      2 | C        |        40 |
+--------+----------+-----------+

Questo sarà il nostro obiettivo, la bella tabella pivot :

select * from history_itemvalue_pivot;

+--------+------+------+------+
| hostid | A    | B    | C    |
+--------+------+------+------+
|      1 |   10 |    3 |    0 |
|      2 |    9 |    0 |   40 |
+--------+------+------+------+

Valori in history.hostid la colonna diventerà valori y nella tabella pivot. Valori in history.itemname la colonna diventerà valori x (per ovvi motivi).

Quando devo risolvere il problema della creazione di una tabella pivot, lo affronto utilizzando un processo in tre fasi (con un quarto passaggio opzionale):

  1. seleziona le colonne di interesse, ovvero valori y e valori x
  2. estendere la tabella di base con colonne aggiuntive, una per ogni valore x
  3. raggruppa e aggrega la tabella estesa:un gruppo per ogni valore y
  4. (opzionale) abbellire la tabella aggregata

Applichiamo questi passaggi al tuo problema e vediamo cosa otteniamo:

Passaggio 1:seleziona le colonne di interesse . Nel risultato desiderato, hostid fornisce i valori y e itemname fornisce i valori x .

Passaggio 2:estendi la tabella di base con colonne aggiuntive . In genere abbiamo bisogno di una colonna per valore x. Ricordiamo che la nostra colonna x-value è itemname :

create view history_extended as (
  select
    history.*,
    case when itemname = "A" then itemvalue end as A,
    case when itemname = "B" then itemvalue end as B,
    case when itemname = "C" then itemvalue end as C
  from history
);

select * from history_extended;

+--------+----------+-----------+------+------+------+
| hostid | itemname | itemvalue | A    | B    | C    |
+--------+----------+-----------+------+------+------+
|      1 | A        |        10 |   10 | NULL | NULL |
|      1 | B        |         3 | NULL |    3 | NULL |
|      2 | A        |         9 |    9 | NULL | NULL |
|      2 | C        |        40 | NULL | NULL |   40 |
+--------+----------+-----------+------+------+------+

Nota che non abbiamo modificato il numero di righe:abbiamo semplicemente aggiunto colonne extra. Nota anche lo schema di NULL s -- una riga con itemname = "A" ha un valore non nullo per la nuova colonna A e valori null per le altre nuove colonne.

Passaggio 3:raggruppa e aggrega la tabella estesa . Dobbiamo group by hostid , poiché fornisce i valori y:

create view history_itemvalue_pivot as (
  select
    hostid,
    sum(A) as A,
    sum(B) as B,
    sum(C) as C
  from history_extended
  group by hostid
);

select * from history_itemvalue_pivot;

+--------+------+------+------+
| hostid | A    | B    | C    |
+--------+------+------+------+
|      1 |   10 |    3 | NULL |
|      2 |    9 | NULL |   40 |
+--------+------+------+------+

(Nota che ora abbiamo una riga per valore y.) Ok, ci siamo quasi! Dobbiamo solo sbarazzarci di quei brutti NULL s.

Fase 4:abbellire . Sostituiremo solo qualsiasi valore nullo con zero in modo che il set di risultati sia più bello da vedere:

create view history_itemvalue_pivot_pretty as (
  select 
    hostid, 
    coalesce(A, 0) as A, 
    coalesce(B, 0) as B, 
    coalesce(C, 0) as C 
  from history_itemvalue_pivot 
);

select * from history_itemvalue_pivot_pretty;

+--------+------+------+------+
| hostid | A    | B    | C    |
+--------+------+------+------+
|      1 |   10 |    3 |    0 |
|      2 |    9 |    0 |   40 |
+--------+------+------+------+

E abbiamo finito:abbiamo creato una bella tabella pivot utilizzando MySQL.

Considerazioni sull'applicazione di questa procedura:

  • quale valore utilizzare nelle colonne extra. Ho usato itemvalue in questo esempio
  • quale valore "neutro" utilizzare nelle colonne extra. Ho usato NULL , ma potrebbe anche essere 0 o "" , a seconda della tua situazione esatta
  • quale funzione di aggregazione utilizzare durante il raggruppamento. Ho usato sum , ma count e max sono spesso usati anche (max viene spesso utilizzato durante la creazione di "oggetti" di una riga che erano stati distribuiti su più righe)
  • utilizzare più colonne per i valori y. Questa soluzione non si limita all'utilizzo di una singola colonna per i valori y:basta inserire le colonne extra nel group by clausola (e non dimenticare di select loro)

Limiti noti:

  • questa soluzione non consente n colonne nella tabella pivot:ogni colonna pivot deve essere aggiunta manualmente quando si estende la tabella di base. Quindi per 5 o 10 valori x, questa soluzione è buona. Per 100, non così bello. Esistono alcune soluzioni con stored procedure che generano una query, ma sono brutte e difficili da correggere. Al momento non conosco un buon modo per risolvere questo problema quando la tabella pivot deve avere molte colonne.