CASE
Jika kasus Anda sesederhana yang ditunjukkan, CASE
pernyataan akan dilakukan:
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;
Tidak masalah apakah Anda menggunakan sum()
, max()
atau min()
sebagai fungsi agregat dalam kueri luar. Semuanya menghasilkan nilai yang sama dalam kasus ini.
crosstab()
Dengan lebih banyak kategori akan lebih sederhana dengan crosstab()
pertanyaan. Ini juga harus lebih cepat untuk tabel yang lebih besar .
Anda perlu menginstal modul tambahan tablefunc (sekali per basis data). Sejak Postgres 9.1 itu sesederhana:
CREATE EXTENSION tablefunc;
Detail dalam jawaban terkait ini:
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);
Tidak ada sqlfiddle untuk yang ini karena situs tidak mengizinkan modul tambahan.
Tolok ukur
Untuk memverifikasi klaim saya, saya menjalankan benchmark cepat dengan data yang mendekati nyata di database pengujian kecil saya. PostgreSQL 9.1.6. Uji dengan EXPLAIN ANALYZE
, terbaik dari 10:
Uji penyiapan dengan 10020 baris:
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;
Hasil:
@bluefeet
Total waktu proses:95.401 md
@wildplasser
(hasil berbeda, termasuk baris dengan count <= 3
)
Total waktu proses:64,497 md
@Andreiy
(+ ORDER BY
)
&@Erwin1 - CASE
(keduanya berkinerja hampir sama)
Total waktu proses:39,105 md
@Erwin2 - crosstab()
Total waktu proses:17,644 md
Sebagian besar hasil proporsional (tetapi tidak relevan) dengan hanya 20 baris. Hanya CTE @wildplasser yang memiliki lebih banyak overhead dan sedikit lonjakan.
Dengan lebih dari beberapa baris, crosstab()
dengan cepat memimpin. Permintaan @ Andreiy bekerja hampir sama dengan versi sederhana saya, fungsi agregat di SELECT
luar (min()
, max()
, sum()
) tidak membuat perbedaan yang terukur (hanya dua baris per grup).
Semuanya seperti yang diharapkan, tidak ada kejutan, lakukan pengaturan saya dan coba @home.