PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Chuyển đổi các hàng và cột (còn gọi là pivot) chỉ với COUNT () tối thiểu?

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.

SQL Fiddle

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.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. PostgreSQL 11 - Thủ tục

  2. Làm cách nào để bạn tìm được số hàng cho tất cả các bảng của mình trong Postgres

  3. Công cụ dịch Oracle PL / SQL sang Postgresql PL / pgSQL

  4. JPA 2.1 StoredProcedureQuery với PostgreSQL và REF_CURSORs

  5. SQL:Đảo ngược chuyển vị một bảng