Bagaimana Anda mendapatkan baris yang berisi nilai maks untuk setiap set yang dikelompokkan?
Saya telah melihat beberapa variasi yang terlalu rumit pada pertanyaan ini, dan tidak ada yang memiliki jawaban yang bagus. Saya telah mencoba menyusun contoh yang paling sederhana:
Diberikan tabel seperti itu di bawah ini, dengan orang, grup, dan kolom usia, bagaimana Anda mendapatkan orang tertua di setiap grup? (Dasi dalam grup harus memberikan hasil alfabet pertama)
Person | Group | Age
---
Bob | 1 | 32
Jill | 1 | 34
Shawn| 1 | 42
Jake | 2 | 29
Paul | 2 | 36
Laura| 2 | 39
Kumpulan hasil yang diinginkan:
Shawn | 1 | 42
Laura | 2 | 39
mysql
sql
greatest-n-per-group
Yarin
sumber
sumber
Jawaban:
Ada cara super sederhana untuk melakukan ini di mysql:
Ini berfungsi karena di mysql Anda diizinkan untuk tidak mengumpulkan kolom non-grup-oleh, dalam hal ini mysql hanya mengembalikan baris pertama . Solusinya adalah dengan terlebih dahulu memesan data sedemikian rupa sehingga untuk setiap grup, baris yang Anda inginkan adalah yang pertama, kemudian kelompokkan dengan kolom yang Anda inginkan nilainya.
Anda menghindari subkueri rumit yang mencoba menemukan
max()
dll, dan juga masalah mengembalikan beberapa baris ketika ada lebih dari satu dengan nilai maksimum yang sama (seperti jawaban lain akan lakukan)Catatan: Ini adalah solusi mysql-only . Semua database lain yang saya tahu akan melempar kesalahan sintaksis SQL dengan pesan "kolom tidak teragregasi tidak terdaftar dalam grup dengan klausa" atau serupa. Karena solusi ini menggunakan perilaku tidak berdokumen , yang lebih berhati-hati mungkin ingin menyertakan tes untuk menyatakan bahwa tetap berfungsi jika versi MySQL di masa depan mengubah perilaku ini.
Pembaruan versi 5.7:
Sejak versi 5.7,
sql-mode
pengaturan termasukONLY_FULL_GROUP_BY
secara default, jadi untuk membuat ini berfungsi, Anda tidak boleh memiliki opsi ini (edit file opsi untuk server untuk menghapus pengaturan ini).sumber
SELECT
klausa dan tidak dihitung menggunakan fungsi agregat.SELECT
klausa tidak secara fungsional tergantung padaGROUP BY
kolom. Jika dikonfigurasi untuk menerimanya (`ONLY_FULL_GROUP_BY` dinonaktifkan), ini berfungsi seperti versi sebelumnya (yaitu nilai kolom tersebut tidak ditentukan).GROUP BY
mengembun menjadi satu rekaman, tetapi semua bidang akan dipilih secara sewenang-wenang dari catatan. Ini mungkin bahwa MySQL saat ini hanya selalu mengambil baris pertama, tetapi bisa juga memilih setiap baris lain atau bahkan nilai-nilai dari berbagai baris dalam versi masa depan.Solusi yang benar adalah:
Bagaimana itu bekerja:
Ini cocok dengan setiap baris
o
dengan semua baris darib
memiliki nilai yang sama di kolomGroup
dan nilai yang lebih besar di kolomAge
. Baris apa pun yango
tidak memiliki nilai maksimum grup dalam kolomAge
akan cocok dengan satu atau lebih baris darib
.The
LEFT JOIN
membuatnya cocok dengan orang tertua dalam kelompok (termasuk orang-orang yang sendirian di kelompok mereka) dengan deretan penuhNULL
s darib
( 'tidak ada usia terbesar dalam kelompok').Menggunakan
INNER JOIN
membuat baris-baris ini tidak cocok dan mereka diabaikan.The
WHERE
klausul terus hanya baris memilikiNULL
s di bidang diekstrak darib
. Mereka adalah orang tertua dari masing-masing kelompok.Bacaan lebih lanjut
Solusi ini dan banyak lainnya dijelaskan dalam buku SQL Antipatterns: Avoiding the Pitfalls of Database Programming
sumber
o.Age = b.Age
, misalnya jika Paul dari grup 2 aktif pada 39 seperti Laura. Namun, jika kita tidak menginginkan perilaku seperti itu, kita dapat melakukan:ON o.Group = b.Group AND (o.Age < b.Age or (o.Age = b.Age and o.id < b.id))
Anda dapat bergabung melawan subquery yang menarik
MAX(Group)
danAge
. Metode ini portabel di sebagian besar RDBMS.sumber
Group = 2, Age = 20
, subquery akan mengembalikan salah satu dari mereka, tetapiON
klausa gabungan akan cocok dengan keduanya , jadi Anda akan mendapatkan 2 baris kembali dengan grup / umur yang sama meskipun vals berbeda untuk kolom lainnya, bukan satu.Solusi sederhana saya untuk SQLite (dan mungkin MySQL):
Namun itu tidak berfungsi di PostgreSQL dan mungkin beberapa platform lainnya.
Di PostgreSQL Anda dapat menggunakan klausa DISTINCT ON :
sumber
Menggunakan metode peringkat.
sumber
:=
sebelumnya - apa itu?Tidak yakin apakah MySQL memiliki fungsi row_number. Jika demikian, Anda dapat menggunakannya untuk mendapatkan hasil yang diinginkan. Pada SQL Server Anda dapat melakukan sesuatu yang mirip dengan:
sumber
solusi axiac adalah yang paling berhasil bagi saya pada akhirnya. Namun saya memiliki kompleksitas tambahan: "nilai maksimum" yang dihitung, berasal dari dua kolom.
Mari kita gunakan contoh yang sama: Saya ingin orang tertua di setiap grup. Jika ada orang yang sama-sama tua, ambil orang yang paling tinggi.
Saya harus melakukan join kiri dua kali untuk mendapatkan perilaku ini:
Semoga ini membantu! Saya kira seharusnya ada cara yang lebih baik untuk melakukan ini ...
sumber
Solusi saya hanya berfungsi jika Anda hanya perlu mengambil satu kolom, namun untuk kebutuhan saya adalah solusi terbaik yang ditemukan dalam hal kinerja (hanya menggunakan satu permintaan tunggal!):
Ini menggunakan GROUP_CONCAT untuk membuat daftar concat yang diurutkan dan kemudian saya substring hanya yang pertama.
sumber
Saya punya solusi sederhana dengan menggunakan
WHERE IN
sumber
Menggunakan CTE - Ekspresi Tabel Umum:
sumber
Dalam Oracle di bawah ini, kueri dapat memberikan hasil yang diinginkan.
sumber
sumber
Anda juga bisa mencoba
sumber
Saya tidak akan menggunakan Grup sebagai nama kolom karena kata itu dilindungi undang-undang. Namun mengikuti SQL akan berhasil.
sumber
Metode ini memiliki manfaat memungkinkan Anda untuk memberi peringkat dengan kolom yang berbeda, dan tidak merusak data lainnya. Ini cukup berguna dalam situasi di mana Anda mencoba mendaftar pesanan dengan kolom untuk item, daftar yang paling berat terlebih dahulu.
Sumber: http://dev.mysql.com/doc/refman/5.0/id/group-by-functions.html#function_group-concat
sumber
biarkan nama tabel menjadi orang
sumber
Jika ID (dan semua coulmns) diperlukan dari mytable
sumber
Ini adalah bagaimana saya mendapatkan baris N maks per grup di mysql
bagaimana itu bekerja:
co.country = ci.country
) < 1
jadi untuk 3 elemen -) <3co.id < ci.id
Contoh lengkap di sini:
mysql pilih n nilai maksimum per grup
sumber