CASE
Nếu trường hợp của bạn đơn giản như đã trình bày, một CASE
tuyên bố sẽ thực hiện:
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;
Không quan trọng việc bạn sử dụng sum()
, max()
hoặc min()
dưới dạng hàm tổng hợp trong truy vấn bên ngoài. Tất cả chúng đều dẫn đến cùng một giá trị trong trường hợp này.
crosstab()
Với nhiều danh mục hơn, sẽ đơn giản hơn với crosstab()
truy vấn. Điều này cũng sẽ nhanh hơn đối với các bảng lớn hơn .
Bạn cần cài đặt mô-đun bổ sung tablefunc (một lần cho mỗi cơ sở dữ liệu). Kể từ khi Postgres 9.1 đơn giản như sau:
CREATE EXTENSION tablefunc;
Chi tiết trong câu trả lời liên quan này:
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);
Không có sqlfiddle cho cái này vì trang web không cho phép các mô-đun bổ sung.
Điểm chuẩn
Để xác minh các tuyên bố của mình, tôi đã chạy một điểm chuẩn nhanh với dữ liệu gần với dữ liệu thực trong cơ sở dữ liệu thử nghiệm nhỏ của mình. PostgreSQL 9.1.6. Kiểm tra với EXPLAIN ANALYZE
, tốt nhất trong số 10:
Thử nghiệm thiết lập với 10020 hàng:
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;
Kết quả:
@bluefeet
Tổng thời gian chạy:95,401 mili giây
@wildplasser
(các kết quả khác nhau, bao gồm các hàng có count <= 3
)
Tổng thời gian chạy:64,497 mili giây
@Andreiy
(+ ORDER BY
)
&@ Erwin1 - CASE
(cả hai đều hoạt động như nhau)
Tổng thời gian chạy:39,105 mili giây
@ Erwin2 - crosstab()
Tổng thời gian chạy:17,644 mili giây
Kết quả tỷ lệ lớn (nhưng không liên quan) chỉ có 20 hàng. Chỉ CTE của @ wildplasser có chi phí cao hơn và tăng đột biến một chút.
Với nhiều hàng, crosstab()
nhanh chóng dẫn đầu. Truy vấn của @ Andreiy hoạt động giống như phiên bản đơn giản của tôi, hàm tổng hợp trong SELECT
bên ngoài (min()
, max()
, sum()
) không tạo ra sự khác biệt có thể đo lường được (chỉ hai hàng cho mỗi nhóm).
Mọi thứ như mong đợi, không có gì ngạc nhiên, hãy thực hiện thiết lập của tôi và thử nó @home.