CASE
Wenn Ihr Fall so einfach ist wie gezeigt, ein CASE
Anweisung reicht:
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;
Dabei spielt es keine Rolle, ob Sie sum()
verwenden , max()
oder min()
als Aggregatfunktion in der äußeren Abfrage. Sie alle ergeben in diesem Fall den gleichen Wert.
crosstab()
Bei mehr Kategorien wird es mit einer crosstab()
einfacher Anfrage. Dies sollte auch schneller für größere Tabellen sein .
Sie müssen das zusätzliche Modul tablefunc installieren (einmal pro Datenbank). Seit Postgres 9.1 ist das so einfach wie:
CREATE EXTENSION tablefunc;
Details in dieser verwandten Antwort:
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);
Kein sqlfiddle für dieses hier, da die Site keine zusätzlichen Module zulässt.
Benchmark
Um meine Behauptungen zu überprüfen, habe ich in meiner kleinen Testdatenbank einen schnellen Benchmark mit nahezu realen Daten durchgeführt. PostgreSQL 9.1.6. Testen Sie mit EXPLAIN ANALYZE
, Beste von 10:
Testaufbau mit 10020 Zeilen:
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;
Ergebnisse:
@bluefeet
Gesamtlaufzeit:95.401 ms
@wildplasser
(unterschiedliche Ergebnisse, enthält Zeilen mit count <= 3
)
Gesamtlaufzeit:64,497 ms
@Andreiy
(+ ORDER BY
)
&@Erwin1 - CASE
(beide Leistung ungefähr gleich)
Gesamtlaufzeit:39,105 ms
@Erwin2 - crosstab()
Gesamtlaufzeit:17,644 ms
Weitgehend proportionale (aber irrelevante) Ergebnisse mit nur 20 Zeilen. Nur der CTE von @wildplasser hat mehr Overhead und Spitzen.
Bei mehr als einer Handvoll Zeilen crosstab()
übernimmt schnell die Führung. Die Abfrage von @Andreiy funktioniert ungefähr genauso wie meine vereinfachte Version, Aggregatfunktion im äußeren SELECT
(min()
, max()
, sum()
) macht keinen messbaren Unterschied (nur zwei Zeilen pro Gruppe).
Alles wie erwartet, keine Überraschungen, nimm mein Setup und probiere es @home aus.