Penting: Harap pertimbangkan untuk memutakhirkan ke MySQL 8+ dan gunakan fungsi ROW_NUMBER () yang didefinisikan dan didokumentasikan, dan parit peretasan lama yang dikaitkan dengan fitur versi MySQL kuno yang terbatas fitur
Sekarang inilah salah satu dari peretasan itu:
Jawaban di sini yang sebagian besar / semua variabel in-query tampaknya mengabaikan fakta bahwa dokumentasi mengatakan (parafrase):
Jangan mengandalkan item dalam daftar SELECT yang sedang dievaluasi secara berurutan dari atas ke bawah. Jangan menetapkan variabel dalam satu item SELECT dan menggunakannya dalam item lain
Dengan demikian, ada risiko mereka akan mengeluarkan jawaban yang salah, karena mereka biasanya melakukan a
select
(row number variable that uses partition variable),
(assign partition variable)
Jika ini pernah dievaluasi dari bawah ke atas, nomor baris akan berhenti berfungsi (tidak ada partisi)
Jadi kita perlu menggunakan sesuatu dengan urutan eksekusi yang terjamin. Masukkan KASUS KAPAN:
SELECT
t.*,
@r := CASE
WHEN col = @prevcol THEN @r + 1
WHEN (@prevcol := col) = null THEN null
ELSE 1 END AS rn
FROM
t,
(SELECT @r := 0, @prevcol := null) x
ORDER BY col
Sebagai garis besar ld, urutan penugasan prevcol adalah penting - prevcol harus dibandingkan dengan nilai baris saat ini sebelum kami menetapkan nilai dari baris saat ini (jika tidak itu akan menjadi nilai baris baris saat ini, bukan nilai baris baris sebelumnya) .
Inilah cara ini cocok:
KAPAN pertama dievaluasi. Jika col baris ini sama dengan col baris sebelumnya maka @r bertambah dan dikembalikan dari KASUS. Nilai yang dikembalikan ini disimpan dalam @r. Ini adalah fitur MySQL yang tugasnya mengembalikan nilai baru dari apa yang ditugaskan ke @r ke dalam baris hasil.
Untuk baris pertama pada set hasil, @prevcol adalah nol (ini diinisialisasi ke nol dalam subquery) sehingga predikat ini salah. Predikat pertama ini juga mengembalikan false setiap kali perubahan col (baris saat ini berbeda dengan baris sebelumnya). Ini menyebabkan WHEN kedua dievaluasi.
Predikat WHEN kedua selalu salah, dan itu ada murni untuk menetapkan nilai baru ke @prevcol. Karena col baris ini berbeda dengan col baris sebelumnya (kita tahu ini karena jika itu sama, WHEN pertama akan digunakan), kita harus menetapkan nilai baru untuk menyimpannya untuk pengujian di waktu berikutnya. Karena tugas dibuat dan kemudian hasil tugas dibandingkan dengan nol, dan apa pun yang disamakan dengan nol salah, predikat ini selalu salah. Tetapi setidaknya mengevaluasi itu melakukan tugasnya menjaga nilai col dari baris ini, sehingga dapat dievaluasi terhadap nilai col baris berikutnya
Karena WHEN kedua salah, itu berarti dalam situasi di mana kolom kita dipartisi oleh (col) telah berubah, itu adalah ELSE yang memberikan nilai baru untuk @r, memulai kembali penomoran dari 1
Kita ini sampai pada situasi di mana ini:
SELECT
t.*,
ROW_NUMBER() OVER(PARTITION BY pcol1, pcol2, ... pcolX ORDER BY ocol1, ocol2, ... ocolX) rn
FROM
t
Memiliki bentuk umum:
SELECT
t.*,
@r := CASE
WHEN col1 = @pcol1 AND col2 = @pcol2 AND ... AND colX = @pcolX THEN @r + 1
WHEN (@pcol1 := pcol1) = null OR (@pcol2 := col2) = null OR ... OR (@pcolX := colX) = null THEN null
ELSE 1
END AS rn
FROM
t,
(SELECT @r := 0, @pcol1 := null, @pcol2 := null, ..., @pcolX := null) x
ORDER BY pcol1, pcol2, ..., pcolX, ocol1, ocol2, ..., ocolX
Catatan kaki:
P dalam pcol berarti "partisi", o dalam ocol berarti "urutan" - dalam bentuk umum saya menjatuhkan "prev" dari nama variabel untuk mengurangi kekacauan visual
Kurung di sekitar (@pcolX := colX) = null
itu penting. Tanpa mereka Anda akan menetapkan nol untuk @pcolX dan semuanya berhenti berfungsi
Ini kompromi bahwa set hasil harus dipesan oleh kolom partisi juga, untuk kolom sebelumnya dibandingkan untuk bekerja. Dengan demikian, Anda tidak dapat memesan nomor yang Anda tentukan sesuai dengan satu kolom, tetapi hasil yang Anda tentukan dipesan ke yang lain. Anda mungkin dapat menyelesaikan ini dengan subkueri tapi saya percaya dokumen tersebut juga menyatakan bahwa pemesanan subkueri dapat diabaikan kecuali LIMIT digunakan dan ini dapat berdampak kinerja
Saya belum menyelidiki lebih jauh dari pengujian bahwa metode ini bekerja, tetapi jika ada risiko bahwa predikat di SAAT kedua akan dioptimalkan jauh (apa pun dibandingkan dengan nol adalah nol / salah jadi mengapa repot-repot menjalankan tugas) dan tidak dieksekusi , itu juga berhenti. Ini sepertinya tidak terjadi dalam pengalaman saya, tetapi saya akan dengan senang hati menerima komentar dan mengusulkan solusi jika itu bisa terjadi
Mungkin bijaksana untuk memberikan nulls yang membuat @pcolX ke tipe kolom Anda yang sebenarnya, dalam subquery yang membuat variabel @pcolX, yaitu: select @pcol1 := CAST(null as INT), @pcol2 := CAST(null as DATE)
greatest-n-per-group
untuk memandu Anda ke pertanyaan serupa.