Ada banyak pertanyaan serupa yang dapat ditemukan di sini tapi saya tidak berpikir ada yang menjawab pertanyaan dengan memadai.
Saya akan melanjutkan dari pertanyaan paling populer saat ini dan menggunakan contoh mereka jika tidak apa-apa.
Tugas dalam hal ini adalah untuk mendapatkan posting terbaru untuk setiap penulis dalam database.
Contoh query menghasilkan hasil yang tidak dapat digunakan karena tidak selalu posting terbaru yang dikembalikan.
SELECT wp_posts.* FROM wp_posts
WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
ORDER BY wp_posts.post_date DESC
Jawaban yang diterima saat ini adalah
SELECT
wp_posts.*
FROM wp_posts
WHERE
wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
HAVING wp_posts.post_date = MAX(wp_posts.post_date) <- ONLY THE LAST POST FOR EACH AUTHOR
ORDER BY wp_posts.post_date DESC
Sayangnya jawaban ini jelas dan salah sederhana dan dalam banyak kasus menghasilkan hasil yang kurang stabil daripada permintaan asli.
Solusi terbaik saya adalah menggunakan subquery formulir
SELECT wp_posts.* FROM
(
SELECT *
FROM wp_posts
ORDER BY wp_posts.post_date DESC
) AS wp_posts
WHERE wp_posts.post_status='publish'
AND wp_posts.post_type='post'
GROUP BY wp_posts.post_author
Pertanyaan saya adalah pertanyaan sederhana: Apakah ada yang memesan baris sebelum pengelompokan tanpa menggunakan subquery?
Sunting : Pertanyaan ini merupakan kelanjutan dari pertanyaan lain dan kekhasan situasi saya sedikit berbeda. Anda dapat (dan harus) berasumsi bahwa ada juga wp_posts.id yang merupakan pengidentifikasi unik untuk pos tertentu itu.
sumber
post_author
danpost_date
tidak cukup untuk mendapatkan baris unik, jadi harus ada lebih banyak untuk mendapatkan baris unik perpost_author
There are plenty of similar questions to be found on here but I don't think that any answer the question adequately.
Itulah gunanya hadiah.Jawaban:
Menggunakan suatu
ORDER BY
dalam subquery bukanlah solusi terbaik untuk masalah ini.Solusi terbaik untuk mendapatkan
max(post_date)
oleh penulis adalah dengan menggunakan subquery untuk mengembalikan tanggal max dan kemudian bergabung dengan itu ke meja Anda padapost_author
tanggal max dan.Solusinya harus:
Jika Anda memiliki data sampel berikut:
Subquery akan mengembalikan tanggal maksimum dan pembuat:
Kemudian karena Anda bergabung kembali ke tabel, pada kedua nilai Anda akan mengembalikan detail lengkap dari posting itu.
Lihat SQL Fiddle dengan Demo .
Untuk memperluas komentar saya tentang menggunakan subquery untuk mengembalikan data ini dengan akurat.
MySQL tidak memaksa Anda untuk
GROUP BY
setiap kolom yang Anda sertakan dalamSELECT
daftar. Akibatnya, jika Anda hanyaGROUP BY
satu kolom tetapi mengembalikan total 10 kolom, tidak ada jaminan bahwa nilai kolom lain milik milikpost_author
yang dikembalikan. Jika kolom tidak ada dalamGROUP BY
MySQL, pilih nilai apa yang harus dikembalikan.Menggunakan subquery dengan fungsi agregat akan menjamin bahwa penulis dan posting yang benar dikembalikan setiap waktu.
Sebagai catatan, sementara MySQL memungkinkan Anda untuk menggunakan
ORDER BY
dalam subquery dan memungkinkan Anda untuk menerapkanGROUP BY
tidak setiap kolom dalamSELECT
daftar perilaku ini tidak diperbolehkan di database lain termasuk SQL Server.sumber
wp_posts
di kedua kolom untuk mendapatkan baris penuh.GROUP BY
hanya satu kolom, tidak ada jaminan bahwa nilai-nilai di kolom lain akan secara konsisten benar. Sayangnya, MySQL memungkinkan jenis SELECT / GROUPing ini terjadi, sedangkan produk lain tidak. Dua, sintaks menggunakanORDER BY
subquery saat diizinkan di MySQL tidak diperbolehkan dalam produk database lain termasuk SQL Server. Anda harus menggunakan solusi yang akan mengembalikan hasil yang tepat setiap kali dieksekusi.INDEX(post_author, post_date)
ini penting.post_id
pertanyaan dalam Anda, maka secara teknis Anda harus mengelompokkannya juga, yang kemungkinan besar akan memengaruhi hasil Anda.Solusi Anda menggunakan ekstensi untuk klausa GROUP BY yang memungkinkan untuk dikelompokkan berdasarkan beberapa bidang (dalam hal ini, adil
post_author
):dan pilih kolom yang tidak teragregasi:
yang tidak tercantum dalam grup dengan klausa, atau yang tidak digunakan dalam fungsi agregat (MIN, MAX, COUNT, dll.).
Penggunaan ekstensi yang benar untuk klausa GROUP BY
Ini berguna ketika semua nilai kolom non-agregat sama untuk setiap baris.
Sebagai contoh, misalkan Anda memiliki meja
GardensFlowers
(name
taman,flower
yang tumbuh di taman):dan Anda ingin mengekstrak semua bunga yang tumbuh di taman, di mana banyak bunga tumbuh. Maka Anda harus menggunakan subquery, misalnya Anda bisa menggunakan ini:
Jika Anda perlu mengekstrak semua bunga yang merupakan satu-satunya bunga di garder, Anda dapat mengubah kondisi HAVING
HAVING COUNT(DISTINCT flower)=1
, tetapi MySql juga memungkinkan Anda untuk menggunakan ini:tidak ada subquery, bukan SQL standar, tetapi lebih sederhana.
Penggunaan ekstensi yang salah untuk klausa GROUP BY
Tetapi apa yang terjadi jika Anda PILIH kolom non-agregat yang tidak sama untuk setiap baris? Nilai apa yang dipilih MySql untuk kolom itu?
Sepertinya MySql selalu memilih nilai PERTAMA yang dijumpainya.
Untuk memastikan bahwa nilai pertama yang dihadapinya persis dengan nilai yang Anda inginkan, Anda perlu menerapkan
GROUP BY
kueri yang dipesan, maka kebutuhan untuk menggunakan subquery. Anda tidak bisa melakukannya sebaliknya.Dengan asumsi bahwa MySql selalu memilih baris pertama yang dihadapinya, Anda dengan benar menyortir baris sebelum GROUP BY. Namun sayangnya, jika Anda membaca dokumentasi dengan cermat, Anda akan melihat bahwa asumsi ini tidak benar.
Saat memilih kolom non-agregat yang tidak selalu sama, MySql bebas memilih nilai apa pun, sehingga nilai yang dihasilkannya benar-benar tidak dapat ditentukan .
Saya melihat bahwa trik ini untuk mendapatkan nilai pertama dari kolom non-agregat banyak digunakan, dan biasanya / hampir selalu berhasil, saya kadang-kadang menggunakannya juga (dengan risiko saya sendiri). Tetapi karena itu tidak didokumentasikan, Anda tidak dapat mengandalkan perilaku ini.
Tautan ini (terima kasih ypercube!) Trik GROUP BY telah dioptimalkan menunjukkan situasi di mana permintaan yang sama mengembalikan hasil yang berbeda antara MySql dan MariaDB, mungkin karena mesin pengoptimalan yang berbeda.
Jadi, jika trik ini berhasil, itu hanya masalah keberuntungan.
The jawaban yang diterima pada pertanyaan lain terlihat salah kepada saya:
wp_posts.post_date
adalah kolom non-agregat, dan nilainya akan secara resmi tidak ditentukan, tetapi kemungkinan akan menjadi yang pertamapost_date
ditemui. Tetapi karena trik GROUP BY diterapkan pada tabel yang tidak berurutan, tidak yakin yang mana yang pertama kalipost_date
ditemukan.Mungkin akan mengembalikan posting yang merupakan satu-satunya posting dari penulis tunggal, tetapi bahkan ini tidak selalu pasti.
Solusi yang memungkinkan
Saya pikir ini bisa menjadi solusi yang mungkin:
Pada permintaan dalam, saya mengembalikan tanggal posting maksimum untuk setiap penulis. Saya kemudian mempertimbangkan fakta bahwa penulis yang sama secara teori dapat memiliki dua posting pada saat yang sama, jadi saya hanya mendapatkan ID maksimum. Dan kemudian saya mengembalikan semua baris yang memiliki ID maksimum itu. Itu bisa dibuat lebih cepat menggunakan gabungan bukannya klausa IN.
(Jika Anda yakin itu
ID
hanya meningkat, dan jikaID1 > ID2
juga berarti demikianpost_date1 > post_date2
, maka kueri dapat dibuat lebih sederhana, tapi saya tidak yakin apakah ini masalahnya).sumber
extension to GROUP By
bacaan yang menarik, terima kasih untuk itu.Apa yang akan Anda baca agak basi, jadi jangan coba ini di rumah!
Dalam SQL secara umum jawaban untuk pertanyaan Anda adalah TIDAK , tetapi karena mode santai dari
GROUP BY
(disebutkan oleh @bluefeet ), jawabannya adalah YA di MySQL.Misalkan, Anda memiliki indeks BTREE pada (post_status, post_type, post_author, post_date). Bagaimana indeks terlihat seperti di bawah tenda?
(post_status = 'publikasikan', post_type = 'posting', post_author = 'pengguna A', post_date = '2012-12-01') (post_status = 'publikasikan', post_type = 'posting', post_author = 'pengguna A', post_date = '2012-12-31') (post_status = 'publish', post_type = 'post', post_author = 'pengguna B', post_date = '2012-10-01') (post_status = 'publish', post_type = ' posting ', post_author =' pengguna B ', post_date =' 2012-12-01 ')
Itu adalah data yang diurutkan berdasarkan semua bidang itu dalam urutan menaik.
Ketika Anda melakukan secara
GROUP BY
default itu mengurutkan data oleh bidang pengelompokan (post_author
, dalam kasus kami; post_status, post_type diperlukan olehWHERE
klausa) dan jika ada indeks yang cocok, dibutuhkan data untuk setiap catatan pertama dalam urutan menaik. Itu adalah permintaan yang akan mengambil yang berikut (posting pertama untuk setiap pengguna):(post_status = 'publikasikan', post_type = 'posting', post_author = 'pengguna A', post_date = '2012-12-01') (post_status = 'publikasikan', post_type = 'posting', post_author = 'pengguna B', post_date = '2012-10-01')
Tetapi
GROUP BY
di MySQL, Anda dapat menentukan urutan secara eksplisit. Dan ketika Anda memintapost_user
dalam urutan menurun, itu akan berjalan melalui indeks kami dalam urutan yang berlawanan, masih mengambil catatan pertama untuk setiap kelompok yang sebenarnya terakhir.Itu adalah
akan memberi kita
(post_status = 'publikasikan', post_type = 'posting', post_author = 'pengguna B', post_date = '2012-12-01') (post_status = 'publikasikan', post_type = 'posting', post_author = 'pengguna A', post_date = '2012-12-31')
Sekarang, ketika Anda memesan hasil pengelompokan dengan post_date, Anda mendapatkan data yang Anda inginkan.
NB :
Ini bukan yang saya sarankan untuk permintaan khusus ini. Dalam hal ini, saya akan menggunakan versi yang sedikit dimodifikasi dari apa yang disarankan @bluefeet . Namun teknik ini mungkin sangat berguna. Lihatlah jawaban saya di sini: Mengambil catatan terakhir di setiap kelompok
Perangkap : Kelemahan dari pendekatan ini adalah itu
Keuntungannya adalah kinerja dalam hard case. Dalam hal ini, kinerja kueri harus sama dengan dalam permintaan @ bluefeet, karena jumlah data yang terlibat dalam penyortiran (semua data dimuat ke tabel sementara dan kemudian disortir; btw, kuerinya memerlukan
(post_status, post_type, post_author, post_date)
indeks juga) .Apa yang saya sarankan :
Seperti yang saya katakan, pertanyaan-pertanyaan itu membuat MySQL membuang waktu berpotensi data dalam jumlah besar dalam tabel sementara. Jika Anda perlu paging (yaitu LIMIT terlibat) sebagian besar data bahkan dibuang. Apa yang akan saya lakukan adalah meminimalkan jumlah data yang diurutkan: yaitu mengurutkan dan membatasi data minimum dalam subquery dan kemudian bergabung kembali ke seluruh tabel.
Permintaan yang sama menggunakan pendekatan yang dijelaskan di atas:
Semua pertanyaan itu dengan rencana eksekusi mereka pada SQLFiddle .
sumber
Coba yang ini. Hanya dapatkan daftar tanggal posting terbaru dari masing-masing penulis . Itu dia
sumber
post_date IN (select max(...) ...)
. Ini lebih efisien daripada melakukan grup dalam sub pilih, lihat dev.mysql.com/doc/refman/5.6/en/subquery-optimization.htmlIN ( SELECT ... )
jauh lebih efisien daripada GABUNGAN setara.Tidak. Tidak masuk akal untuk memesan catatan sebelum pengelompokan, karena pengelompokan akan mengubah set hasil. Cara subquery adalah cara yang disukai. Jika ini berjalan terlalu lambat Anda harus mengubah desain tabel Anda, misalnya dengan menyimpan id dari posting terakhir untuk setiap penulis dalam tabel terpisah, atau memperkenalkan kolom boolean yang menunjukkan untuk setiap penulis yang posnya adalah yang terakhir satu.
sumber
Cukup gunakan fungsi maks dan fungsi grup
sumber
Sekadar rekap, solusi standar menggunakan subquery yang tidak berkorelasi dan terlihat seperti ini:
Jika Anda menggunakan versi kuno MySQL, atau kumpulan data yang cukup kecil, maka Anda dapat menggunakan metode berikut:
sumber
** Sub kueri mungkin berdampak buruk pada kinerja bila digunakan dengan kumpulan data besar **
Permintaan asli
Kueri yang dimodifikasi
karena saya menggunakan
max
diselect clause
==>max(p.post_date)
adalah mungkin untuk menghindari permintaan pilih sub dan memesan dengan kolom max setelah grup oleh.sumber
Pertama, jangan gunakan * dalam pilih, memengaruhi kinerja mereka dan menghalangi penggunaan grup berdasarkan dan dipesan oleh. Coba kueri ini:
Ketika Anda tidak menentukan tabel dalam ORDER BY, hanya alias, mereka akan memesan hasil pilih.
sumber