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

Gunakan sesuatu seperti TOP dengan GROUP BY

Anda dapat dengan mudah mengambil penumpang dengan nama terpanjang per grup dengan DISTINCT ON .

Tapi saya tidak melihat cara untuk menggabungkan itu (atau cara sederhana lainnya) dengan kueri asli Anda dalam satu SELECT . Saya sarankan untuk bergabung dengan dua subkueri terpisah:

SELECT *
FROM  (  -- your original query
   SELECT orig
        , count(*) AS flight_cnt
        , count(distinct passenger) AS pass_cnt
        , percentile_cont(0.5) WITHIN GROUP (ORDER BY bags) AS bag_cnt_med
   FROM   table1
   GROUP  BY orig
   ) org_query
JOIN  (  -- my addition
   SELECT DISTINCT ON (orig) orig, passenger AS pass_max_len_name
   FROM   table1
   ORDER  BY orig, length(passenger) DESC NULLS LAST
   ) pas USING (orig);

USING dalam klausa join dengan mudah hanya menampilkan satu instance orig , jadi Anda cukup menggunakan SELECT * di bagian luar SELECT .

Jika passenger bisa NULL, penting untuk menambahkan NULLS LAST :

Dari beberapa nama penumpang dengan panjang maksimum yang sama dalam grup yang sama, Anda mendapatkan pilihan sewenang-wenang - kecuali jika Anda menambahkan lebih banyak ekspresi ke ORDER BY sebagai pemutus ikatan. Penjelasan terperinci dalam jawaban yang ditautkan di atas.

Kinerja?

Biasanya, pemindaian tunggal lebih unggul, terutama dengan pemindaian berurutan.

Kueri di atas menggunakan dua scan (mungkin scan indeks / indeks saja). Tetapi pemindaian kedua relatif murah kecuali tabelnya terlalu besar untuk dimasukkan ke dalam cache (kebanyakan). Lukas menyarankan kueri alternatif dengan hanya tunggal SELECT menambahkan:

, (ARRAY_AGG (passenger ORDER BY LENGTH (passenger) DESC))[1]  -- I'd add NULLS LAST

Idenya cerdas, tetapi terakhir kali saya menguji , array_agg dengan ORDER BY tidak berkinerja baik. (Overhead per grup ORDER BY substansial, dan penanganan array juga mahal.)

Pendekatan yang sama bisa lebih murah dengan fungsi agregat khusus first() seperti yang diinstruksikan dalam Wiki Postgres di sini . Atau, lebih cepat lagi, dengan versi yang ditulis dalam C, tersedia di PGXN . Menghilangkan biaya tambahan untuk penanganan array, tetapi kita masih membutuhkan ORDER BY per grup . Mungkin lebih cepat hanya untuk beberapa kelompok. Anda kemudian akan menambahkan:

 , first(passenger ORDER BY length(passenger) DESC NULLS LAST)

Gordon dan Lukas sebutkan juga fungsi jendela first_value() . Fungsi jendela diterapkan setelah fungsi agregat. Untuk menggunakannya dalam SELECT yang sama , kita perlu menggabungkan passenger entah bagaimana first - catch 22. Gordon menyelesaikan ini dengan subquery - kandidat lain untuk performa bagus dengan Postgres standar.

first() melakukan hal yang sama tanpa subquery dan harus lebih sederhana dan sedikit lebih cepat. Tapi tetap tidak akan lebih cepat dari DISTINCT ON yang terpisah untuk sebagian besar kasus dengan beberapa baris per grup. Untuk banyak baris per grup, teknik CTE rekursif biasanya lebih cepat. Masih ada teknik yang lebih cepat jika Anda memiliki tabel terpisah yang menampung semua orig yang relevan dan unik nilai-nilai. Detail:

Solusi terbaik tergantung pada berbagai faktor. Buktinya puding ada di makan. Untuk mengoptimalkan kinerja, Anda harus menguji dengan pengaturan Anda. Kueri di atas harus menjadi yang tercepat.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Mengindeks Nilai Null di PostgreSQL

  2. PostgreSQL:Buat indeks pada stempel waktu::DATE

  3. Rencana Kueri Postgres mengapa estimasi Baris sangat salah

  4. Menggunakan Postgresql dengan Grails :Urutan atau tabel yang hilang:hibernate_sequence

  5. Postgres mengubah urutan secara manual