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

Query PHP e Mysql, usa PHP per convertire righe in colonne

È possibile ottenere il risultato utilizzando una query SQL, ma non è banale.

Ma prima di intraprendere quella strada, ti consiglio di considerare un approccio diverso.

Poiché la query restituisce un set di righe relativamente piccolo, puoi invece recuperare l'intero set di risultati in PHP come un array bidimensionale.

Considera un caso abbastanza semplice, a titolo esemplificativo:

SELECT foo, fee, fi, fo, fum
  FROM mytable 
 ORDER BY foo

foo fee fi  fo  fum
--- --- --- --- ---
ABC   2   3   5   7
DEF  11  13  17  19

Potremmo eseguire un fetchAll e ottenere un array bidimensionale, quindi scorrere l'array e recuperare i valori in base alla colonna, anziché in base alla riga. Un'opzione è trasformare l'array che riceviamo in un nuovo array simile a questo:

bar  ABC  DEF
---  ---  ---
fee    2   11
fi     3   13
fo     5   17
fum    7   19

Non è davvero necessario eseguire la trasformazione, potresti percorrere l'array originale. Ma separare la trasformazione come un passaggio separato probabilmente renderebbe il tuo codice un po' più semplice, quando arrivi a generare effettivamente l'output nella pagina. (Sembra un problema abbastanza comune che qualcuno abbia probabilmente scritto una funzione che esegua la trasformazione dell'array desiderata. Non credo che ci sia un builtin PHP che lo faccia.

titoli:

array { [0]=>'bar'  [1]=>'ABC'  [2]=>'DEF' }

righe:

array {
  [0]=>array { [0]=>'fee'   [1]=>'2'  [2]=>'11' }
  [1]=>array { [0]=>'fi'    [1]=>'3'  [2]=>'13' }
  [2]=>array { [0]=>'fo'    [1]=>'5'  [2]=>'17' }
  [3]=>array { [0]=>'fum'   [1]=>'7'  [2]=>'19' }
}

Per un piccolo insieme di righe come te, sceglierei di farlo in PHP anziché in SQL.

Ma hai chiesto come farlo in SQL. Come ho detto prima, non è banale.

SQL richiede che l'istruzione SELECT definisca ogni colonna da restituire; il numero ei tipi delle colonne non possono essere dinamici quando l'istruzione viene eseguita.

Se costruiamo un'altra query (a parte la query originale) che definisce le colonne e restituisce le righe che ci aspettiamo restituite con i segnaposto per i valori, siamo a metà strada. Non resta che eseguire un outer join alle righe restituite dalla query originale e restituire condizionalmente i valori delle colonne sulle righe appropriate.

Questo approccio funziona se hai un set predefinito di righe e colonne che dobbiamo restituire, specialmente quando l'origine della riga originale è sparsa e dobbiamo generare le righe "mancanti". (Ad esempio, ottenendo il conteggio dei prodotti ordinati e ci sono molte righe mancanti, non c'è un buon modo per generare le righe mancanti.

Ad esempio:

SELECT r.bar
     , '' AS `ABC`
     , '' AS `DEF`
  FROM ( SELECT 'fee' AS bar
          UNION ALL SELECT 'fi'
          UNION ALL SELECT 'fo'
          UNION ALL SELECT 'fum'
       ) r
 GROUP BY r.bar

Quello tornerà:

 bar  ABC  DEF
 ---  ---  ---
 fee
 fi
 fo
 fum

Quindi, questo ci ottiene tutte le colonne definite e tutte le righe che vogliamo restituire. La prima colonna è popolata. Quella query non ha ancora davvero bisogno di GROUP BY, ma ne avremo bisogno una volta che avremo abbinato le righe dal set di risultati di origine "reale".

Il "trucco" ora è abbinare le righe dalla nostra fonte e restituire il valore da una colonna in base alle condizioni appropriate.

Quello che andremo a generare è essenzialmente un set di risultati simile a questo:

bar  foo  ABC  DEF
---  ---  ---  ---
fee  ABC    2
fee  DEF        11
fi   ABC    3
fi   DEF        13
fo   ABC    5
fo   DEF        15
fum  ABC    7
fum  DEF        17

Quindi andremo a "comprimere" le righe, rimuovendo la colonna foo dal set di risultati ed eseguendo un GROUP BY su bar . Utilizzeremo una funzione di aggregazione (MAX o SUM) sfruttando la gestione che fanno con i valori NULL, per produrre un risultato come questo:

bar  foo  ABC  DEF
---  ---  ---  ---
fee         2   11
fi          3   13
fo          5   15
fum         7   17

Usando questo SQL piuttosto ingombrante:

SELECT r.bar
     , MAX(CASE WHEN t.foo = 'ABC' THEN CASE r.bar 
         WHEN 'fee' THEN t.fee 
         WHEN 'fi'  THEN t.fi
         WHEN 'fo'  THEN t.fo
         WHEN 'fum' THEN t.fum
       END END) AS 'ABC'
     , MAX(CASE WHEN t.foo = 'DEF' THEN CASE r.bar 
         WHEN 'fee' THEN t.fee 
         WHEN 'fi'  THEN t.fi
         WHEN 'fo'  THEN t.fo
         WHEN 'fum' THEN t.fum
       END END) AS 'DEF'
  FROM ( SELECT 'foo' AS col
          UNION ALL SELECT 'fee'
          UNION ALL SELECT 'fi'
          UNION ALL SELECT 'fo'
          UNION ALL SELECT 'fum'
       ) r
 CROSS
  JOIN mysource t
 GROUP BY r.bar

Nota che mysource nella query sopra può essere sostituita con una vista in linea, avvolgendo le parentesi attorno a una query adatta che restituisce le righe desiderate.

La vista in linea alias come r è la nostra fonte per restituire le righe che vogliamo restituire.

Le espressioni nell'elenco SELECT stanno eseguendo i test condizionali, per selezionare i valori corretti per ogni colonna in ogni riga.

Dato il modello regolare delle istruzioni CASE, è possibile utilizzare alcuni SQL per aiutare a generare la query, ma questo deve essere fatto come un passaggio separato. L'output di tale SQL può essere utilizzato per aiutare a formare la query effettiva di cui abbiamo bisogno.

Nel tuo caso, dato che workdate è quello che vuoi usare per l'intestazione della colonna, probabilmente questo dovrà essere generato dinamicamente. (Puoi eliminare la seconda colonna "giorno della settimana" dalla query di origine originale e spostarla nella query esterna.

Se non conoscessi il workdate valori per le intestazioni prima di eseguire la query, quindi sceglierei di creare una TABELLA TEMPORANEA e popolarla con i risultati della query originale, quindi eseguire una query sulla tabella temporanea per ottenere la workdate intestazioni e la "prima colonna" per generare le righe. Quindi eseguirei la query effettiva sulla tabella temporanea.

Per ripetere, penso che faresti meglio a fare la trasformazione/pivot dei risultati dalla tua query originale in PHP, piuttosto che provare a farlo in SQL.