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

Pilih baris pertama di setiap grup GROUP BY?

DISTINCT ON biasanya paling sederhana dan tercepat untuk ini di PostgreSQL .
(Untuk pengoptimalan kinerja untuk beban kerja tertentu, lihat di bawah.)

SELECT DISTINCT ON (customer)
       id, customer, total
FROM   purchases
ORDER  BY customer, total DESC, id;

Atau lebih pendek (jika tidak sejelas) dengan nomor urut kolom keluaran:

SELECT DISTINCT ON (2)
       id, customer, total
FROM   purchases
ORDER  BY 2, 3 DESC, 1;

Jika total bisa NULL (tidak ada salahnya, tetapi Anda ingin mencocokkan indeks yang ada):

...
ORDER  BY customer, total DESC NULLS LAST, id;

Poin utama

DISTINCT ON adalah ekstensi PostgreSQL dari standar (di mana hanya DISTINCT secara keseluruhan SELECT daftar ditentukan).

Buat daftar sejumlah ekspresi dalam DISTINCT ON klausa, nilai baris gabungan mendefinisikan duplikat. Panduan:

Jelas, dua baris dianggap berbeda jika mereka berbeda dalam setidaknya satu nilai kolom. Nilai nol dianggap sama dalam perbandingan ini.

Penekanan saya yang berani.

DISTINCT ON dapat digabungkan dengan ORDER BY . Ekspresi terdepan dalam ORDER BY harus dalam kumpulan ekspresi di DISTINCT ON , tetapi Anda dapat mengatur ulang urutan di antara mereka dengan bebas. Contoh.
Anda dapat menambahkan tambahan ekspresi ke ORDER BY untuk memilih baris tertentu dari setiap kelompok rekan-rekan. Atau, seperti yang dikatakan manual:

DISTINCT ON ekspresi harus cocok dengan ORDER BY paling kiri ekspresi. ORDER BY klausa biasanya akan berisi ekspresi tambahan yang menentukan prioritas baris yang diinginkan dalam setiap DISTINCT ON grup.

Saya menambahkan id sebagai item terakhir untuk memutuskan ikatan:
"Pilih baris dengan id terkecil dari setiap grup berbagi total tertinggi ."

Untuk mengurutkan hasil dengan cara yang tidak sesuai dengan urutan pengurutan yang menentukan yang pertama per grup, Anda dapat menyarangkan kueri di atas dalam kueri luar dengan ORDER BY lain . Contoh.

Jika total bisa NULL, Anda kemungkinan besar ingin baris dengan nilai bukan nol terbesar. Tambahkan NULLS LAST seperti yang didemonstrasikan. Lihat:

  • Urutkan menurut kolom ASC, tetapi nilai NULL terlebih dahulu?

SELECT daftar tidak dibatasi oleh ekspresi dalam DISTINCT ON atau ORDER BY dengan cara apapun. (Tidak diperlukan dalam kasus sederhana di atas):

  • Anda tidak perlu sertakan ekspresi apa pun di DISTINCT ON atau ORDER BY .

  • Anda bisa sertakan ekspresi lain di SELECT daftar. Ini penting untuk mengganti kueri yang jauh lebih kompleks dengan subkueri dan fungsi agregat/jendela.

Saya menguji dengan Postgres versi 8.3 – 13. Tetapi fitur tersebut telah ada setidaknya sejak versi 7.1, jadi pada dasarnya selalu.

Indeks

sempurna indeks untuk kueri di atas akan menjadi indeks multi-kolom yang mencakup ketiga kolom dalam urutan yang cocok dan dengan urutan pengurutan yang cocok:

CREATE INDEX purchases_3c_idx ON purchases (customer, total DESC, id);

Mungkin terlalu terspesialisasi. Tetapi gunakan jika kinerja baca untuk kueri tertentu sangat penting. Jika Anda memiliki DESC NULLS LAST dalam kueri, gunakan yang sama dalam indeks sehingga urutan pengurutan cocok dan indeks dapat diterapkan.

Efektivitas / Pengoptimalan kinerja

Timbang biaya dan manfaat sebelum membuat indeks yang disesuaikan untuk setiap kueri. Potensi indeks di atas sangat bergantung pada distribusi data .

Indeks digunakan karena memberikan data yang telah diurutkan sebelumnya. Di Postgres 9.2 atau lebih baru, kueri juga dapat memanfaatkan pemindaian indeks saja jika indeks lebih kecil dari tabel yang mendasarinya. Namun, indeks harus dipindai secara keseluruhan.

Untuk sedikit baris per pelanggan (kardinalitas tinggi di kolom customer ), ini sangat efisien. Terlebih lagi jika Anda tetap membutuhkan output yang diurutkan. Manfaatnya berkurang dengan bertambahnya jumlah baris per pelanggan.
Idealnya, Anda memiliki cukup work_mem untuk memproses langkah pengurutan yang terlibat dalam RAM dan tidak tumpah ke disk. Tapi secara umum setting work_mem juga tinggi dapat memiliki efek samping. Pertimbangkan SET LOCAL untuk pertanyaan yang sangat besar. Temukan berapa banyak yang Anda butuhkan dengan EXPLAIN ANALYZE . Menyebutkan "Disk: " dalam langkah pengurutan menunjukkan perlunya lebih banyak:

  • Parameter konfigurasi work_mem di PostgreSQL di Linux
  • Optimalkan kueri sederhana menggunakan ORDER BY tanggal dan teks

Untuk banyak baris per pelanggan (kardinalitas rendah di kolom customer ), pemindaian indeks longgar (alias "lewati pemindaian") akan (jauh) lebih efisien, tetapi itu tidak diterapkan hingga Postgres 14. (Implementasi untuk pemindaian hanya indeks sedang dikembangkan untuk Postgres 15. Lihat di sini dan di sini.)
Untuk sekarang, ada teknik kueri yang lebih cepat untuk menggantikan ini. Khususnya jika Anda memiliki tabel terpisah yang menampung pelanggan unik, yang merupakan kasus penggunaan khas. Tetapi juga jika Anda tidak:

  • SELECT DISTINCT lebih lambat dari yang diharapkan pada tabel saya di PostgreSQL
  • Optimalkan kueri GROUP BY untuk mengambil baris terbaru per pengguna
  • Optimalkan kueri maksimum berdasarkan grup
  • Kueri N baris terkait terakhir per baris

Tolok Ukur

Lihat jawaban terpisah.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Apa yang setara dengan PostgreSQL untuk ISNULL()

  2. PostgreSQL 12:Kunci Asing dan Tabel yang Dipartisi

  3. Driver QPSQL tidak dimuat Qt

  4. Pernyataan Postgresql Sederhana - nama kolom tidak ada

  5. Heroku Rails 4 tidak dapat terhubung ke server:koneksi ditolak