CASE
Se il tuo caso è semplice come illustrato, un CASE
la dichiarazione farà:
SELECT year
, sum(CASE WHEN animal = 'kittens' THEN price END) AS kittens
, sum(CASE WHEN animal = 'puppies' THEN price END) AS puppies
FROM (
SELECT year, animal, avg(price) AS price
FROM tab_test
GROUP BY year, animal
HAVING count(*) > 2
) t
GROUP BY year
ORDER BY year;
Non importa se usi sum()
, max()
o min()
come funzione aggregata nella query esterna. In questo caso danno tutti lo stesso valore.
crosstab()
Con più categorie sarà più semplice con un crosstab()
interrogazione. Questo dovrebbe anche essere più veloce per i tavoli più grandi .
È necessario installare il modulo aggiuntivo tablefunc (una volta per database). Da Postgres 9.1 è semplice come:
CREATE EXTENSION tablefunc;
Dettagli in questa risposta correlata:
SELECT * FROM crosstab(
'SELECT year, animal, avg(price) AS price
FROM tab_test
GROUP BY animal, year
HAVING count(*) > 2
ORDER BY 1,2'
,$$VALUES ('kittens'::text), ('puppies')$$)
AS ct ("year" text, "kittens" numeric, "puppies" numeric);
Nessun sqlfiddle per questo perché il sito non consente moduli aggiuntivi.
Parametro
Per verificare le mie affermazioni, ho eseguito un rapido benchmark con dati quasi reali nel mio piccolo database di test. PostgreSQL 9.1.6. Prova con EXPLAIN ANALYZE
, al meglio di 10:
Configurazione di prova con 10020 righe:
CREATE TABLE tab_test (year int, animal text, price numeric);
-- years with lots of rows
INSERT INTO tab_test
SELECT 2000 + ((g + random() * 300))::int/1000
, CASE WHEN (g + (random() * 1.5)::int) %2 = 0 THEN 'kittens' ELSE 'puppies' END
, (random() * 200)::numeric
FROM generate_series(1,10000) g;
-- .. and some years with only few rows to include cases with count < 3
INSERT INTO tab_test
SELECT 2010 + ((g + random() * 10))::int/2
, CASE WHEN (g + (random() * 1.5)::int) %2 = 0 THEN 'kittens' ELSE 'puppies' END
, (random() * 200)::numeric
FROM generate_series(1,20) g;
Risultati:
@bluefeet
Durata totale:95.401 ms
@wildplasser
(risultati diversi, include righe con count <= 3
)
Durata totale:64.497 ms
@Andreiy
(+ ORDER BY
)
&@Erwin1 - CASE
(entrambi funzionano più o meno allo stesso modo)
Durata totale:39.105 ms
@Erwin2 - crosstab()
Durata totale:17.644 ms
Risultati ampiamente proporzionali (ma irrilevanti) con solo 20 righe. Solo il CTE di @wildplasser ha più sovraccarico e picchi leggermente.
Con più di una manciata di righe, crosstab()
prende rapidamente il comando.@La query di Andreiy funziona più o meno come la mia versione semplificata, funzione di aggregazione in esterno SELECT
(min()
, max()
, sum()
) non fa alcuna differenza misurabile (solo due righe per gruppo).
Tutto come previsto, nessuna sorpresa, prendi il mio setup e provalo a casa.