CASE
Pokud je váš případ tak jednoduchý, jak je ukázáno, CASE
příkaz udělá:
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;
Nezáleží na tom, zda použijete sum()
, max()
nebo min()
jako agregační funkce ve vnějším dotazu. Všechny vedou v tomto případě ke stejné hodnotě.
crosstab()
S více kategoriemi to bude jednodušší s crosstab()
dotaz. To by také mělo být rychlejší pro větší stoly .
Musíte nainstalovat další modul tablefunc (jednou pro každou databázi). Od Postgres 9.1 je to tak jednoduché jako:
CREATE EXTENSION tablefunc;
Podrobnosti v této související odpovědi:
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);
Žádné sqlfiddle pro tento, protože web nepovoluje další moduly.
Srovnávací
Abych ověřil svá tvrzení, provedl jsem rychlý benchmark s téměř reálnými daty v mé malé testovací databázi. PostgreSQL 9.1.6. Otestujte pomocí EXPLAIN ANALYZE
, nejlepší z 10:
Testovací nastavení s 10020 řádky:
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;
Výsledky:
@bluefeet
Celková doba běhu:95,401 ms
@wildplasser
(různé výsledky, zahrnuje řádky s count <= 3
)
Celková doba běhu:64,497 ms
@Andreiy
(+ ORDER BY
)
&@Erwin1 - CASE
(obě mají přibližně stejný výkon)
Celková doba běhu:39,105 ms
@Erwin2 - crosstab()
Celková doba běhu:17,644 ms
Do značné míry proporcionální (ale irelevantní) výsledky pouze s 20 řádky. Pouze @wildplasser's CTE má větší režii a trochu stoupá.
S více než hrstkou řádků, crosstab()
rychle přebírá vedení.@Andreiyho dotaz funguje přibližně stejně jako moje zjednodušená verze, agregační funkce ve vnějším SELECT
(min()
, max()
, sum()
) nečiní žádný měřitelný rozdíl (jen dva řádky na skupinu).
Vše podle očekávání, žádná překvapení, vezměte si moje nastavení a vyzkoušejte to @home.