PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Transpose baris dan kolom (alias pivot) hanya dengan minimum COUNT()?

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.

SQL Fiddle

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.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Klon catatan, lalu gunakan id kenaikan otomatisnya untuk operasi lebih lanjut

  2. Membuat Pengaturan Replikasi PostgreSQL di Debian / Ubuntu

  3. PHPUnit:Bagaimana cara menguji interaksi basis data di server Postgres jarak jauh?

  4. Postgres:agregat kolom ke dalam array

  5. Buat string dari array