CASE
Si votre cas est aussi simple que démontré, un CASE
déclaration fera :
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;
Peu importe que vous utilisiez sum()
, max()
ou min()
en tant que fonction d'agrégation dans la requête externe. Ils donnent tous la même valeur dans ce cas.
crosstab()
Avec plus de catégories ce sera plus simple avec un crosstab()
requête. Cela devrait également être plus rapide pour les grandes tables .
Vous devez installer le module supplémentaire tablefunc (une fois par base de données). Depuis Postgres 9.1, c'est aussi simple que :
CREATE EXTENSION tablefunc;
Détails dans cette réponse connexe :
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);
Pas de sqlfiddle pour celui-ci car le site n'autorise pas les modules supplémentaires.
Référence
Pour vérifier mes affirmations, j'ai exécuté un benchmark rapide avec des données proches de la réalité dans ma petite base de données de test. PostgreSQL 9.1.6. Testez avec EXPLAIN ANALYZE
, meilleur des 10 :
Configuration du test avec 10 020 lignes :
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;
Résultats :
@bluefeet
Autonomie totale :95,401 ms
@wildplasser
(différents résultats, inclut des lignes avec count <= 3
)
Autonomie totale :64,497 ms
@Andreiy
(+ ORDER BY
)
&@Erwin1 - CASE
(les deux fonctionnent à peu près de la même manière)
Durée d'exécution totale :39,105 ms
@Erwin2 - crosstab()
Autonomie totale :17,644 ms
Résultats largement proportionnels (mais non pertinents) avec seulement 20 lignes. Seul le CTE de @wildplasser a plus de frais généraux et un peu de pics.
Avec plus d'une poignée de lignes, crosstab()
prend rapidement les devants. La requête de @Andreiy fonctionne à peu près de la même manière que ma version simplifiée, fonction d'agrégation dans SELECT
externe (min()
, max()
, sum()
) ne fait aucune différence mesurable (seulement deux lignes par groupe).
Tout comme prévu, pas de surprises, prenez ma configuration et essayez-la à la maison.