Torna all'elenco degli articoli Articoli
Tempo di lettura: 6 minuti

Tabelle pivot in PostgreSQL con la funzione Crosstab

Alcuni anni fa, quando è stata rilasciata la versione 8.3 di PostgreSQL, è stata introdotta una nuova estensione chiamata tablefunc. Questa estensione fornisce un insieme di funzioni davvero interessanti. Una di queste è la funzione crosstab, utilizzata per la creazione di tabelle pivot. È questo l'argomento che tratteremo in questo articolo.

Il modo più semplice per spiegare il funzionamento di questa funzione è utilizzare un esempio con una tabella pivot. Prima spiegheremo il nostro punto iniziale da un punto di vista pratico, poi definiremo la tabella pivot desiderata.

Il nostro punto iniziale: Dati grezzi

Mentre leggete questo articolo, immaginate di essere un insegnante di una scuola elementare. Supponiamo che insegniate tutte le materie (lingua, musica, ecc.). La scuola fornisce un sistema per registrare tutti i risultati delle valutazioni o dei test. La seguente istruzione SQL mostrerà i risultati delle valutazioni che avete precedentemente caricato nel sistema:

SELECT *
FROM evaluations


StudentSubjectEvaluation_resultEvaluation_day
Smith, JohnMusic7.02016-03-01
Smith, JohnMaths4.02016-03-01
Smith, JohnHistory9.02016-03-22
Smith, JohnLanguage7.02016-03-15
Smith, JohnGeography9.02016-03-04
Gabriel, PeterMusic2.02016-03-01
Gabriel, PeterMaths10.02016-03-01
Gabriel, PeterHistory7.02016-03-22
Gabriel, PeterLanguage4.02016-03-15
Gabriel, PeterGeography10.02016-03-04

Il nostro obiettivo: una tabella pivot

La seguente griglia potrebbe facilmente tenere traccia dei progressi degli studenti. In informatica, questo tipo di griglia si chiama tabella pivot. Se si analizza il modo in cui viene costruita la tabella pivot, si scopre che vengono utilizzati i valori dei dati grezzi come intestazioni di colonna o nomi di campi (in questo caso, geografia, storia, matematica, ecc.).

StudentGeographyHistoryLanguageMathsMusic
Gabriel, Peter10.07.04.010.02.0
Smith, John9.09.07.04.07.0

Abilitazione della funzione Crosstab

Come abbiamo già detto, la funzione crosstab fa parte di un'estensione di PostgreSQL chiamata tablefunc. Per richiamare la funzione crosstab, è necessario abilitare l'estensione tablefunc eseguendo il seguente comando SQL:

CREATE extension tablefunc;

Abilitazione dell'estensione tablefunc con il comando SQL, tabella pivot postgresql

Come funziona la funzione Crosstab

La funzione crosstab riceve come parametro un comando SQL SELECT, che deve rispettare le seguenti restrizioni:

  • La SELECT deve restituire 3 colonne.
  • La prima colonna della SELECT sarà l'identificatore di ogni riga della tabella pivot o del risultato finale. Nel nostro esempio, si tratta del nome dello studente. Si noti che i nomi degli studenti (John Smith e Peter Gabriel) appaiono nella prima colonna.
  • La seconda colonna della SELECT rappresenta le categorie della tabella pivot. Nel nostro esempio, queste categorie sono le materie scolastiche. È importante notare che i valori di questa colonna si espandono in molte colonne della tabella pivot. Se la seconda colonna restituisce cinque valori diversi (geografia, storia e così via), la tabella pivot avrà cinque colonne.
  • La terza colonna della SELECT rappresenta il valore da assegnare a ciascuna cella della tabella pivot. Questi sono i risultati della valutazione nel nostro esempio.

Se pensiamo alla nostra tabella pivot come a un array bidimensionale, la prima colonna SELECT è la prima dimensione dell'array, la seconda colonna SELECT è la seconda dimensione e la terza è il valore dell'elemento dell'array , come griglia [valore_prima_colonna, valore_seconda_colonna] = valore_terza_colonna.

Nel nostro esempio, il parametro SELECT sarà:

SELECT student, subject, evaluation_result FROM evaluations ORDER BY 1,2

La funzione crosstab viene invocata nella clausola FROM dell'istruzione SELECT. È necessario definire i nomi delle colonne e i tipi di dati che verranno inseriti nel risultato finale. Per i nostri scopi, il risultato finale è definito come:

AS final_result(Student TEXT, Geography NUMERIC,History NUMERIC,Language NUMERIC,Maths NUMERIC,Music NUMERIC)

Unendo tutti questi pezzi, la nostra query finale sarà:

SELECT * 
FROM crosstab( 'select student, subject, evaluation_result from evaluations order by 1,2') 
     AS final_result(Student TEXT, Geography NUMERIC,History NUMERIC,Language NUMERIC,Maths NUMERIC,Music NUMERIC);

E possiamo vedere il risultato qui:

StudentGeographyHistoryLanguageMathsMusic
Gabriel, Peter10.07.04.010.02.0
Smith, John9.09.07.04.07.0

Un set di dati grezzi, molte tabelle pivot

Da un unico set di dati, possiamo produrre molte tabelle pivot diverse. Continuiamo con l'esempio dell'insegnante e della classe e vediamo alcune delle opzioni disponibili.

Esempio 1: Medie mensili di valutazione

Come insegnanti, potremmo aver bisogno di un rapporto sui risultati della valutazione di uno studente per l'anno in corso. Ad esempio, supponiamo di voler ottenere le valutazioni medie di John Smith da marzo a luglio. In una griglia come la seguente, la tabella avrebbe il seguente aspetto:

month textgeography numerichistory numericlanguage numericmaths numericmusic numeric
39.009.007.004.007.00
44.007.507.004.005.66
58.006.007.007.007.00
67.507.007.007.008.00
76.669.007.7510.006.00

L'SQL per questa tabella pivot è:

SELECT * 
FROM crosstab( 'select extract(month from period)::text, subject.name,
		     trunc(avg(evaluation_result),2) 
     from evaluation, subject  
     where evaluation.subject_id = subject.subject_id and student_id = 1 
     group by 1,2 order by 1,2')  AS final_result(Month TEXT, Geography NUMERIC,History NUMERIC,Language NUMERIC,Maths NUMERIC,Music NUMERIC);

Confrontate questo risultato con la tabella precedente:

MonthGeographyHistoryLanguageMathsMusic
39.009.007.004.007.00
44.007.507.004.005.66
58.006.007.007.007.00
67.507.007.007.008.00
76.669.007.7510.006.00

Esempio 2: Trovare i record incompleti degli studenti

Potremmo anche intitolare questa sezione "Una limitazione di Crosstab e come risolverla". Prima di entrare nel merito, prepariamo la scena:

Supponiamo di voler vedere se alcuni studenti non hanno un punteggio di valutazione per determinate materie. Si potrebbe provare la nostra query precedente, aggiungendo una clausola WHERE per il mese di luglio. Il codice sarebbe simile a questo:

SELECT * 
FROM crosstab( 'select student, subject, evaluation_result from evaluations where extract (month from evaluation_day) = 7 order by 1,2') 
     AS final_result(Student TEXT, Geography NUMERIC,History NUMERIC,Language NUMERIC,Maths NUMERIC,Music NUMERIC);

La seguente tabella pivot è il risultato di questa query. Possiamo subito notare che non abbiamo voti per lingua, matematica e musica per Peter.

StudentGeographyHistoryLanguageMathsMusic
Gabriel, Peter10.06.07.00
Smith, John6.08.06.09.04.0

Tuttavia, se proviamo a eseguire una query regolare per ottenere i voti di Peter a luglio ...

SELECT * from evaluations 
where extract ( month from evaluation_day)=7 and student like 'Gabriel, Peter'

... otteniamo risultati diversi. Qui abbiamo i voti per la geografia e la lingua:

studentsubjectevaluation_resultevaluation_day
Gabriel, PeterLanguage6.02016-07-15
Gabriel, PeterGeography10.02016-07-04

Naturalmente, la seconda query è quella corretta perché mostra i dati grezzi. Il problema è nel processo di creazione della tabella pivot: alcune categorie mancano di informazioni. Per risolvere questo problema, possiamo utilizzare la funzione crosstab con un secondo parametro, che rappresenta l'elenco completo delle categorie. Se ci sono valori mancanti, la tabella pivot verrà comunque costruita correttamente. (Ho evidenziato in rosso la query con il secondo parametro).

SELECT * 
FROM crosstab( 'select student, subject, evaluation_result from evaluations 
                where extract (month from evaluation_day) = 7 order by 1,2',
                'select name from subject order by 1') 
     AS final_result(Student TEXT, Geography NUMERIC,History NUMERIC,Language NUMERIC,Maths NUMERIC,Music NUMERIC);

Ora abbiamo una tabella pivot corretta con i valori vuoti al posto giusto.

StudentGeographyHistoryLanguageMathsMusic
Gabriel, Peter10.0--6.0--
Smith, John6.08.06.09.04.0

Esercizio

Le tabelle pivot ci offrono un modo diverso di vedere i nostri dati. Inoltre, è possibile creare diverse tabelle pivot basate sugli stessi dati grezzi utilizzando la funzione crosstab. Provate a creare una tabella pivot che mostri la temperatura massima per ogni città e mese sulla base dei dati grezzi della tabella sottostante.

CREATE TABLE weather (city text, when timestamp, temperature float);

CityWhenTemperature
Miami2016-01-01 08:00:0068.6
Miami2016-01-21 08:00:0073.3
Orlando2016-01-01 08:00:0072.5
Miami2016-02-01 18:00:0058.6
Orlando2016-02-02 18:00:0062.5
Miami2016-03-03 08:00:0055.6
Orlando2016-03-03 08:00:0056.7
Miami2016-04-04 18:00:0050.6
Orlando2016-04-04 18:00:0061.5

La tabella pivot deve avere una riga per ogni città e una colonna per ogni mese. Se volete, potete pensare ad altre tabelle pivot da realizzare con gli stessi dati.