CASE
Om ditt fall är så enkelt som visat, ett CASE
uttalandet kommer att göra:
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;
Det spelar ingen roll om du använder sum()
, max()
eller min()
som aggregerad funktion i den yttre frågan. De resulterar alla i samma värde i det här fallet.
crosstab()
Med fler kategorier blir det enklare med en crosstab()
fråga. Detta bör också vara snabbare för större bord .
Du måste installera tilläggsmodulen tablefunc (en gång per databas). Sedan Postgres 9.1 är det så enkelt som:
CREATE EXTENSION tablefunc;
Detaljer i detta relaterade svar:
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);
Ingen sqlfiddle för denna eftersom webbplatsen inte tillåter ytterligare moduler.
Benchmark
För att verifiera mina påståenden körde jag en snabb benchmark med nästan verkliga data i min lilla testdatabas. PostgreSQL 9.1.6. Testa med EXPLAIN ANALYZE
, bäst av 10:
Testinställning med 10020 rader:
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;
Resultat:
@bluefeet
Total körtid:95,401 ms
@wildplasser
(olika resultat, inkluderar rader med count <= 3
)
Total körtid:64,497 ms
@Andreiy
(+ ORDER BY
)
&@Erwin1 - CASE
(båda presterar ungefär likadant)
Total körtid:39,105 ms
@Erwin2 - crosstab()
Total körtid:17,644 ms
Till stor del proportionella (men irrelevanta) resultat med endast 20 rader. Bara @wildplassers CTE har mer overhead och spikar lite.
Med mer än en handfull rader, crosstab()
tar snabbt ledningen.@Andreiys fråga utför ungefär samma som min förenklade version, aggregatfunktion i yttre SELECT
(min()
, max()
, sum()
) gör ingen mätbar skillnad (bara två rader per grupp).
Allt som förväntat, inga överraskningar, ta min installation och prova @home.