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.