ROW_NUMBER () di MySQL

281

Apakah ada cara yang bagus di MySQL untuk mereplikasi fungsi SQL Server ROW_NUMBER()?

Sebagai contoh:

SELECT 
    col1, col2, 
    ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS intRow
FROM Table1

Lalu saya bisa, misalnya, menambahkan syarat untuk membatasi intRowke 1 untuk mendapatkan satu baris dengan tertinggi col3untuk setiap (col1, col2)pasangan.

Paul
sumber
9
Ditandai dengan greatest-n-per-groupuntuk memandu Anda ke pertanyaan serupa.
Bill Karwin
1
Sql-ServerTag dihapus karena ini adalah item pilihan tertinggi pada pencarian tag gabungan tetapi tidak benar-benar relevan dengan SQL Server.
Martin Smith
untuk fungsi nomor baris mysql sederhana, periksa datamakessense.com/mysql-rownum-row-number-function
AdrianBR
Untuk MySQL, satu-satunya solusi efisien dibahas di mysql.rjweb.org/doc.php/groupwise_max . Banyak solusi yang dipublikasikan membutuhkan pemindaian tabel lengkap (atau lebih buruk).
Rick James
2
Solusi tanpa mempromosikan diri tanpa malu-malu untuk fungsi analitis nyata ROW_NUMBER, RANK, DESNSE_RANK di MySQL
Kenneth Xu

Jawaban:

102

Saya ingin baris dengan col3 tunggal tertinggi untuk setiap pasangan (col1, col2).

Itu maksimum groupwise , salah satu pertanyaan SQL yang paling sering ditanyakan (karena sepertinya itu harus mudah, tetapi sebenarnya itu jenis tidak).

Saya sering mengisi untuk null-self-join:

SELECT t0.col3
FROM table AS t0
LEFT JOIN table AS t1 ON t0.col1=t1.col1 AND t0.col2=t1.col2 AND t1.col3>t0.col3
WHERE t1.col1 IS NULL;

"Dapatkan baris dalam tabel yang tidak ada baris lain dengan pencocokan col1, col2 memiliki col3 yang lebih tinggi." (Anda akan melihat ini dan sebagian besar solusi maksimum groupwise lainnya akan mengembalikan beberapa baris jika lebih dari satu baris memiliki col1, col2, col3 yang sama. Jika itu masalah Anda mungkin perlu beberapa post-processing.)

bobince
sumber
2
Tetapi bagaimana jika ada dua nilai maksimal col3 untuk pasangan (col1, col2)? Anda akan berakhir dengan dua baris.
Paul
@ Paul: ya! Baru saja menambahkan catatan tentang itu dalam jawaban beberapa waktu lalu. Anda biasanya dapat dengan mudah menjatuhkan baris tambahan yang tidak diinginkan di lapisan aplikasi setelahnya secara acak, tetapi jika Anda memiliki banyak baris dengan col3 yang sama, itu bisa bermasalah.
bobince
1
bobince, solusinya menjadi agak populer di sini di SO, tapi saya punya pertanyaan. Solusinya pada dasarnya sama seperti jika seseorang akan mencoba untuk menemukan id terbesar dengan permintaan berikut: SELECT t1.id FROM test t1 LEFT JOIN test t2 ON t1.id>t2.id WHERE t2.id IS NULL;Apakah tidak memerlukan n*n/2 + n/2perbandingan IS NULL untuk menemukan baris tunggal? Apakah ada optimasi yang tidak saya lihat? Saya mencoba mengajukan pertanyaan serupa kepada Bill di utas lain tetapi dia tampaknya mengabaikannya.
mulai
2
@ Paul - Untuk mengatasi kasus di mana ada beberapa baris yang cocok dengan maks per grup dan Anda ingin mengambil satu saja, Anda selalu dapat menambahkan kunci utama dalam logika klausa ON untuk memutuskan ikatan ... SELECT t0.col3 FROM table SEBAGAI T0 KIRI BERGABUNG tabel SEBAGAI t1 PADA t0.col1 = t1.col1 DAN t0.col2 = t1.col2 AND (t1.col3, t1.pk)> (t0.col3, t0.pk) DIMANA t1.col1 ADALAH NULL;
Jon Armstrong - Xgc
2
Ini akan lebih mudah dibaca karenaSELECT t0.col3 FROM table AS t0 WHERE NOT EXISTS (select 1 from table AS t1 ON t0.col1=t1.col1 AND t0.col2=t1.col2 AND t1.col3>t0.col3)
wrschneider
204

Tidak ada fungsi peringkat di MySQL. Yang paling dekat yang bisa Anda dapatkan adalah menggunakan variabel:

SELECT t.*, 
       @rownum := @rownum + 1 AS rank
  FROM YOUR_TABLE t, 
       (SELECT @rownum := 0) r

jadi bagaimana cara kerjanya dalam kasus saya? Saya membutuhkan dua variabel, satu untuk masing-masing col1 dan col2? Col2 perlu diatur ulang entah bagaimana ketika col1 berubah ..?

Iya. Jika itu adalah Oracle, Anda bisa menggunakan fungsi LEAD untuk memuncak pada nilai berikutnya. Untungnya, Quassnoi mencakup logika untuk apa yang perlu Anda terapkan di MySQL .

OMG Ponies
sumber
1
Hmm .... jadi bagaimana cara kerjanya dalam kasus saya? Saya membutuhkan dua variabel, satu untuk masing-masing col1 dan col2? Col2 perlu diatur ulang entah bagaimana ketika col1 berubah ..?
Paul
Thanks ... seperti yang saya katakan di atas, jawaban ini sama-sama diterima bobince, tapi saya hanya dapat mencentang satu :-)
Paul
9
Menetapkan dan membaca dari variabel yang ditentukan pengguna dalam pernyataan yang sama tidak dapat diandalkan. ini didokumentasikan di sini: dev.mysql.com/doc/refman/5.0/en/user-variables.html : "Sebagai aturan umum, Anda tidak boleh menetapkan nilai ke variabel pengguna dan membaca nilai dalam pernyataan yang sama. Anda mungkin mendapatkan hasil yang Anda harapkan, tetapi ini tidak dijamin. Urutan evaluasi untuk ekspresi yang melibatkan variabel pengguna tidak terdefinisi dan dapat berubah berdasarkan elemen yang terkandung dalam pernyataan yang diberikan. "
Roland Bouman
1
@Rand: Saya hanya menguji pada set data kecil, tidak memiliki masalah. Sayang sekali MySQL belum membahas fungsionalitasnya - permintaan sudah masuk sejak 2008
OMG Ponies
2
Ini tampaknya perilaku yang tidak terdefinisi seperti yang dicatat Roland. misalnya ini memberikan hasil yang benar-benar salah untuk tabel yang saya coba:SELECT @row_num:=@row_num+1 AS row_number, t.id FROM (SELECT * FROM table1 WHERE col = 264 ORDER BY id) t, (SELECT @row_num:=0) var;
jberryman
81

Saya selalu berakhir dengan pola ini. Diberikan tabel ini:

+------+------+
|    i |    j |
+------+------+
|    1 |   11 |
|    1 |   12 |
|    1 |   13 |
|    2 |   21 |
|    2 |   22 |
|    2 |   23 |
|    3 |   31 |
|    3 |   32 |
|    3 |   33 |
|    4 |   14 |
+------+------+

Anda bisa mendapatkan hasil ini:

+------+------+------------+
|    i |    j | row_number |
+------+------+------------+
|    1 |   11 |          1 |
|    1 |   12 |          2 |
|    1 |   13 |          3 |
|    2 |   21 |          1 |
|    2 |   22 |          2 |
|    2 |   23 |          3 |
|    3 |   31 |          1 |
|    3 |   32 |          2 |
|    3 |   33 |          3 |
|    4 |   14 |          1 |
+------+------+------------+

Dengan menjalankan kueri ini, yang tidak memerlukan variabel apa pun yang ditentukan:

SELECT a.i, a.j, count(*) as row_number FROM test a
JOIN test b ON a.i = b.i AND a.j >= b.j
GROUP BY a.i, a.j

Semoga itu bisa membantu!

Mosty Mostacho
sumber
1
jika kolom adalah VARCHAR atau CHAR, bagaimana Anda bisa mengatasinya dengan struktur ini?
Tushar
3
Anda luar biasa, Mosty, saya mencari ini
luckykrrish
Berikan saja jawaban ini menggunakan logika Anda untuk row_number. Terima kasih.
Utsav
@Tushar operator <, >, <=, >=pegangan CHAR dan VARCHAR jenis data pada urutan abjad; Saya berharap, persis apa yang Anda cari.
alex
1
@AlmazVildanov Anda harus dapat menggunakan kueri ini hanya sebagai subquery untuk menyaring row_numbers <= 2 Dan terima kasih besar untuk jawaban ini Mosty, sempurna!
Zax
61
SELECT 
    @i:=@i+1 AS iterator, 
    t.*
FROM 
    tablename AS t,
    (SELECT @i:=0) AS foo
Peter Johnson
sumber
1
Yang pertama: = tampaknya hilang dari jawaban @OMG Ponies. Terima kasih telah mengirim Peter Johnson ini.
sholsinger
Saya kira (PILIH @i: = 0) SEBAGAI foo harus menjadi tabel pertama dalam pernyataan FROM, terutama jika tabel lain menggunakan sub-seleksi
andig
Mengapa Anda bahkan membutuhkan '.. as foo'?
Tom Chiverton
@ TomChiverton Jika tidak ada, Anda mendapatkan: "Kode Kesalahan: 1248. Setiap tabel turunan harus memiliki alias sendiri"
ExStackChanger
1
Penugasan peringkat di sini benar-benar tidak ditentukan dan ini bahkan tidak menjawab pertanyaan
jberryman
27

Lihat Artikel ini, ini menunjukkan cara meniru SQL ROW_NUMBER () dengan partisi oleh di MySQL. Saya mengalami skenario yang sama ini dalam Implementasi WordPress. Saya membutuhkan ROW_NUMBER () dan tidak ada di sana.

http://www.explodybits.com/2011/11/mysql-row-number/

Contoh dalam artikel ini menggunakan satu partisi per bidang. Untuk mempartisi dengan bidang tambahan Anda dapat melakukan sesuatu seperti ini:

  SELECT  @row_num := IF(@prev_value=concat_ws('',t.col1,t.col2),@row_num+1,1) AS RowNumber
         ,t.col1 
         ,t.col2
         ,t.Col3
         ,t.col4
         ,@prev_value := concat_ws('',t.col1,t.col2)
    FROM table1 t,
         (SELECT @row_num := 1) x,
         (SELECT @prev_value := '') y
   ORDER BY t.col1,t.col2,t.col3,t.col4 

Menggunakan concat_ws menangani null. Saya menguji ini terhadap 3 bidang menggunakan int, tanggal, dan varchar. Semoga ini membantu. Lihat artikel saat itu memecah permintaan ini dan menjelaskannya.

Birch
sumber
1
Luar biasa. Ini sebenarnya partisi. Sangat berguna
Stuart Watt
1
Dibandingkan dengan self join, ini jauh lebih efisien, tetapi ada masalah dengan logika, urutan harus dilakukan sebelum menghitung row_num, concat juga tidak diperlukan. `` `SELECT @row_num: = JIKA (@ prev_col1 = t.col1 DAN @ prev_col2 = t.col2), @ row_num + 1, 1) SEBAGAI RowNumber, t.col1, t.col2, t.col3, t.col4 , @ prev_col1: = t.col1, @ prev_col2: = t.col2 FROM (SELECT * FROM table1 ORDER OLEH col1, col2, col3) t, (SELECT @row_num: = 1, @ prev_col1: = '', @ prev_col2: = '') var `` `
Kenneth Xu
Jika Anda perlu memasukkan ini ke dalam subquery, lalu tambahkan limit 18446744073709551615ke memaksa order byklausa.
xmedeko
concat_wsdengan string kosong ''berbahaya: concat_ws('',12,3) = concat_ws('',1,23). Lebih baik menggunakan pemisah '_'atau menggunakan solusi @Kenneth Xu.
xmedeko
Tautan op sudah mati; arsip tautan di sini
user2426679
25

Dari MySQL 8.0.0dan di atas Anda dapat menggunakan fungsi windowed.

1.4 Apa yang Baru di MySQL 8.0 :

Fungsi jendela.

MySQL sekarang mendukung fungsi jendela yang, untuk setiap baris dari kueri, melakukan perhitungan menggunakan baris yang terkait dengan baris itu. Ini termasuk fungsi seperti RANK (), LAG (), dan NTILE (). Selain itu, beberapa fungsi agregat yang ada sekarang dapat digunakan sebagai fungsi jendela; misalnya, SUM () dan AVG ().

ROW_NUMBER () over_clause :

Mengembalikan jumlah baris saat ini di dalam partisi-nya. Baris angka berkisar dari 1 hingga jumlah baris partisi.

ORDER BY mempengaruhi urutan penomoran baris. Tanpa ORDER BY, penomoran baris tidak pasti.

Demo:

CREATE TABLE Table1(
  id INT AUTO_INCREMENT PRIMARY KEY, col1 INT,col2 INT, col3 TEXT);

INSERT INTO Table1(col1, col2, col3)
VALUES (1,1,'a'),(1,1,'b'),(1,1,'c'),
       (2,1,'x'),(2,1,'y'),(2,2,'z');

SELECT 
    col1, col2,col3,
    ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS intRow
FROM Table1;

Demo DBFiddle

Lukasz Szozda
sumber
1
huh ... akhirnya!
Used_By_Already
15

Saya juga akan memilih solusi Mosty Mostacho dengan sedikit modifikasi pada kode permintaannya:

SELECT a.i, a.j, (
    SELECT count(*) from test b where a.j >= b.j AND a.i = b.i
) AS row_number FROM test a

Yang akan memberikan hasil yang sama:

+------+------+------------+
|    i |    j | row_number |
+------+------+------------+
|    1 |   11 |          1 |
|    1 |   12 |          2 |
|    1 |   13 |          3 |
|    2 |   21 |          1 |
|    2 |   22 |          2 |
|    2 |   23 |          3 |
|    3 |   31 |          1 |
|    3 |   32 |          2 |
|    3 |   33 |          3 |
|    4 |   14 |          1 |
+------+------+------------+

untuk meja:

+------+------+
|    i |    j |
+------+------+
|    1 |   11 |
|    1 |   12 |
|    1 |   13 |
|    2 |   21 |
|    2 |   22 |
|    2 |   23 |
|    3 |   31 |
|    3 |   32 |
|    3 |   33 |
|    4 |   14 |
+------+------+

Dengan satu-satunya perbedaan bahwa kueri tidak menggunakan GABUNG dan KELOMPOK OLEH, mengandalkan bersarang pilih sebagai gantinya.

abcdn
sumber
Apakah ini seharusnya lebih baik? Mereka berdua tampaknya cenderung kuadratik, tapi saya tidak yakin bagaimana menginterpretasikan output EXPLAIN
jberryman
Faktanya, seleksi nested diketahui tidak dioptimalkan dengan sangat baik di MySQL, jadi server ini hanya untuk demonstrasi teknik query. Contoh berbasis variabel di atas berfungsi lebih baik untuk sebagian besar kasus praktis, saya kira.
abcdn
1
Saya tidak yakin ada jawaban berdasarkan variabel yang benar-benar menggunakan perilaku yang didefinisikan ...
jberryman
Maaf, saya tidak yakin saya mendapatkan apa yang Anda maksud dengan "perilaku terdefinisi". Apakah maksud Anda ini tidak bekerja untuk Anda, atau Anda hanya khawatir itu tidak didokumentasikan?
abcdn
1
"Perilaku tidak terdefinisi" berarti bahwa itu tidak didokumentasikan untuk bekerja dan / atau didokumentasikan untuk tidak dijamin untuk bekerja. Lihat kutipan & tautan dokumentasi dalam komentar di halaman ini. Ini mungkin mengembalikan apa yang diinginkan (menebak) / dihipotesiskan / fantasi. Untuk versi tertentu dari implementasi, ekspresi kueri tertentu menggunakan CASE incrementing & using variable telah terbukti berfungsi oleh programmer di Percona dengan melihat kode. Itu bisa berubah dengan rilis apa pun.
philipxy
12

Saya akan mendefinisikan fungsi:

delimiter $$
DROP FUNCTION IF EXISTS `getFakeId`$$
CREATE FUNCTION `getFakeId`() RETURNS int(11)
    DETERMINISTIC
begin
return if(@fakeId, @fakeId:=@fakeId+1, @fakeId:=1);
end$$

maka saya bisa melakukan:

select getFakeId() as id, t.* from table t, (select @fakeId:=0) as t2;

Sekarang Anda tidak memiliki subquery, yang tidak dapat Anda lihat.

Quincy
sumber
Bekerja dengan satu batasan: jika Anda menjalankan kueri beberapa kali, Anda akan mendapatkan fakeIds yang terus meningkat untuk set hasil yang sama
Stephan Richter
Anda dapat mengirim set @fakeId = 0; setiap kali Anda ingin menjalankan kueri, tidak optimal tetapi berfungsi
jmpeace
Masalah yang sangat aneh terjadi jika Anda menghapus DETERMINISTIK. Maka falseId salah saat menggunakan pesanan oleh. Kenapa ini?
Chris Muench
8

permintaan untuk row_number di mysql

set @row_number=0;
select (@row_number := @row_number +1) as num,id,name from sbs
user5528503
sumber
Ini dapat digunakan pada permintaan UPDATE? Saya mencoba tetapi saya mendapatkan kesalahan "data terpotong untuk kolom ...".
Diego
1
Jika ada yang tertarik untuk menggunakannya pada UPDATE, itu harus digunakan sebagai sub-kueri agar bisa berfungsi. UPDATE <tabel> SET <field> = (SELECT \ @row_number: = \ @row_number +1) ORDER OLEH <kolom pesanan Anda>; Kolom pesanan menentukan nilai pemesanan baris.
Diego
8

Tidak ada funtion like rownum, row_num()di MySQL tapi jalannya seperti di bawah ini:

select 
      @s:=@s+1 serial_no, 
      tbl.* 
from my_table tbl, (select @s:=0) as s;
Md. Kamruzzaman
sumber
4

Solusi yang saya temukan untuk bekerja paling baik adalah menggunakan subquery seperti ini:

SELECT 
    col1, col2, 
    (
        SELECT COUNT(*) 
        FROM Table1
        WHERE col1 = t1.col1
        AND col2 = t1.col2
        AND col3 > t1.col3
    ) AS intRow
FROM Table1 t1

Kolom PARTITION BY baru saja dibandingkan dengan '=' dan dipisahkan oleh AND. Kolom ORDER BY akan dibandingkan dengan '<' atau '>', dan dipisahkan oleh OR.

Saya telah menemukan ini sangat fleksibel, bahkan jika itu sedikit mahal.

snydergd
sumber
4

Fungsionalitas rownumber tidak dapat ditiru. Anda mungkin mendapatkan hasil yang Anda harapkan, tetapi kemungkinan besar Anda akan kecewa pada tahap tertentu. Inilah yang dikatakan dokumentasi mysql:

Untuk pernyataan lain, seperti SELECT, Anda mungkin mendapatkan hasil yang Anda harapkan, tetapi ini tidak dijamin. Dalam pernyataan berikut, Anda mungkin berpikir bahwa MySQL akan mengevaluasi @a pertama dan kemudian melakukan penugasan kedua: SELECT @a, @a: = @ a +1, ...; Namun, urutan evaluasi untuk ekspresi yang melibatkan variabel pengguna tidak ditentukan.

Salam, Georgi.

pengguna3503199
sumber
Saya tidak mengikuti. Bagaimana "@i: = @i + 1 sebagai posisi" bukan pengganti langsung untuk "ROW_NUMBER () lebih dari (urutan dengan jumlah (skor) jelas) sebagai posisi"?
Tom Chiverton
1
@TomChiverton Karena perilakunya tidak didefinisikan, seperti yang dikatakan manual di sana.
philipxy
4

MariaDB 10.2 menerapkan "Fungsi Jendela", termasuk RANK (), ROW_NUMBER () dan beberapa hal lainnya:

https://mariadb.com/kb/en/mariadb/window-functions/

Berdasarkan ceramah di Percona Live bulan ini, mereka dioptimalkan dengan cukup baik.

Sintaksnya identik dengan kode dalam Pertanyaan.

Rick James
sumber
2

Saya tidak melihat jawaban sederhana yang meliputi bagian "PARTISI DENGAN" jadi inilah milik saya:

SELECT
    *
FROM (
    select
        CASE WHEN @partitionBy_1 = l THEN @row_number:=@row_number+1 ELSE @row_number:=1 END AS i
        , @partitionBy_1:=l AS p
        , t.*
    from (
        select @row_number:=0,@partitionBy_1:=null
    ) as x
    cross join (
        select 1 as n, 'a' as l
        union all
        select 1 as n, 'b' as l    
        union all
        select 2 as n, 'b' as l    
        union all
        select 2 as n, 'a' as l
        union all
        select 3 as n, 'a' as l    
        union all    
        select 3 as n, 'b' as l    
    ) as t
    ORDER BY l, n
) AS X
where i > 1
  • Klausa ORDER BY harus mencerminkan kebutuhan ROW_NUMBER Anda. Dengan demikian sudah ada batasan yang jelas: Anda tidak dapat memiliki beberapa "emulasi" ROW_NUMBER dari formulir ini secara bersamaan.
  • Urutan "kolom yang dihitung" penting . Jika Anda memiliki mysql menghitung kolom tersebut dalam urutan lain, itu mungkin tidak berfungsi.
  • Dalam contoh sederhana ini saya hanya menempatkan satu tetapi Anda dapat memiliki beberapa bagian "PARTISI DENGAN"

        CASE WHEN @partitionBy_1 = part1 AND @partitionBy_2 = part2 [...] THEN @row_number:=@row_number+1 ELSE @row_number:=1 END AS i
        , @partitionBy_1:=part1 AS P1
        , @partitionBy_2:=part2 AS P2
        [...] 
    FROM (
        SELECT @row_number:=0,@partitionBy_1:=null,@partitionBy_2:=null[...]
    ) as x
Serge Profafilecebook
sumber
1

Sedikit terlambat tetapi juga dapat membantu seseorang yang mencari jawaban ...

Antara contoh baris / row_number - kueri rekursif yang dapat digunakan dalam SQL apa pun:

WITH data(row_num, some_val) AS 
(
 SELECT 1 row_num, 1 some_val FROM any_table --dual in Oracle
  UNION ALL
 SELECT row_num+1, some_val+row_num FROM data WHERE row_num < 20 -- any number
)
SELECT * FROM data
 WHERE row_num BETWEEN 5 AND 10
/

ROW_NUM    SOME_VAL
-------------------
5           11
6           16
7           22
8           29
9           37
10          46
Seni
sumber
2
Maaf tapi sejauh yang saya tahu MySQL tidak mendukung ekspresi tabel umum .
Álvaro González
itu sekarang ... @ ÁlvaroGonzález MySQL 8 hanya mendukung fungsi CTE dan jendela, jadi jawaban ini tidak terlalu masuk akal untuk digunakan dalam versi MySQL yang lebih lama ..
Raymond Nijland
1

Ini memungkinkan fungsionalitas yang sama yang disediakan ROW_NUMBER () DAN PARTITION BY untuk dicapai di MySQL

SELECT  @row_num := IF(@prev_value=GENDER,@row_num+1,1) AS RowNumber
       FirstName, 
       Age,
       Gender,
       @prev_value := GENDER
  FROM Person,
      (SELECT @row_num := 1) x,
      (SELECT @prev_value := '') y
  ORDER BY Gender, Age DESC
Alankar
sumber
1

Juga agak terlambat tetapi hari ini saya memiliki kebutuhan yang sama jadi saya melakukan pencarian di Google dan akhirnya pendekatan umum sederhana ditemukan di sini di artikel Pinal Dave http://blog.sqlauthority.com/2014/03/09/mysql-reset-row -nomor-untuk-setiap-grup-partisi-dengan-baris-nomor /

Saya ingin fokus pada pertanyaan awal Paul (itu adalah masalah saya juga) jadi saya merangkum solusi saya sebagai contoh kerja.

Karena kami ingin mempartisi lebih dari dua kolom, saya akan membuat variabel SET selama iterasi untuk mengidentifikasi jika grup baru dimulai.

SELECT col1, col2, col3 FROM (
  SELECT col1, col2, col3,
         @n := CASE WHEN @v = MAKE_SET(3, col1, col2)
                    THEN @n + 1 -- if we are in the same group
                    ELSE 1 -- next group starts so we reset the counter
                END AS row_number,
         @v := MAKE_SET(3, col1, col2) -- we store the current value for next iteration
    FROM Table1, (SELECT @n := 0, @v := NULL) r -- helper table for iteration with startup values
   ORDER BY col1, col2, col3 DESC -- because we want the row with maximum value
) x WHERE row_number = 1 -- and here we select exactly the wanted row from each group

Angka 3 berarti pada parameter pertama MAKE_SET yang saya inginkan kedua nilai dalam SET (3 = 1 | 2). Tentu saja jika kita tidak memiliki dua atau lebih kolom yang membangun grup, kita dapat menghilangkan operasi MAKE_SET. Konstruksinya persis sama. Ini bekerja untuk saya sesuai kebutuhan. Banyak terima kasih kepada Pinal Dave untuk demonstrasi yang jelas.

Miklos Krivan
sumber
1
Perhatikan bahwa ORDER BYdalam sebuah subquery dapat diabaikan (lihat mariadb.com/kb/en/mariadb/… ). Solusi yang disarankan untuk itu adalah menambahkan LIMIT 18446744073709551615ke subquery, yang memaksa semacam. Namun ini dapat menyebabkan masalah kinerja dan tidak valid untuk tabel yang sangat besar :)
pnomolos
1

Ini juga bisa menjadi solusi:

SET @row_number = 0;

SELECT 
    (@row_number:=@row_number + 1) AS num, firstName, lastName
FROM
    employees
Rishabh Pandey
sumber
Itu tidak melakukan partisi apa pun, dan itu tidak berbeda secara signifikan dengan jawaban yang dikutip lebih tinggi
Caius Jard
1

MySQL telah mendukung ROW_NUMBER () sejak versi 8.0+ .

Jika Anda menggunakan MySQL 8.0 atau lebih baru, periksa fungsi ROW_NUMBER (). Jika tidak, Anda harus mengemulasi fungsi ROW_NUMBER ().

Row_number () adalah fungsi peringkat yang mengembalikan nomor urut baris, mulai dari 1 untuk baris pertama.

untuk versi yang lebih lama,

SELECT t.*, 
       @rowid := @rowid + 1 AS ROWID
  FROM TABLE t, 
       (SELECT @rowid := 0) dummy;
Mohideen bin Mohammed
sumber
1

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) = nullitu 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)

Caius Jard
sumber
Tidak ada pembenaran untuk ini. Sama seperti jawaban lain yang ditugaskan & dibaca dari variabel yang sama.
philipxy
Bisakah Anda memberikan lebih banyak detail phil?
Caius Jard
Lihat komentar saya yang lain di halaman ini. Situs Googling ': stackoverflow.com "philipxy" variabel mysql (atur ATAU penugasan ATAU penugasan ATAU tulis) baca': Sebuah jawaban oleh saya & laporan bug yang ditautkan dalam komentar oleh saya di pertanyaan ini di mana jawaban yang diterima mengutip manual belum dengan segera di klaim tidak apa-apa untuk melakukan sesuatu yang bertentangan dengannya. Baca manual ulang variabel & tugas ulang.
philipxy
Saya memahami keprihatinan Anda
Caius Jard
0

Ini bukan solusi yang paling kuat - tetapi jika Anda hanya ingin membuat peringkat dipartisi pada bidang dengan hanya beberapa nilai yang berbeda, mungkin tidak akan sulit untuk menggunakan beberapa kasus ketika logika dengan variabel sebanyak yang Anda butuhkan.

Sesuatu seperti ini telah bekerja untuk saya di masa lalu:

SELECT t.*, 
   CASE WHEN <partition_field> = @rownum1 := @rownum1 + 1 
     WHEN <partition_field> = @rownum2 := @rownum2 + 1 
     ...
     END AS rank
FROM YOUR_TABLE t, 
   (SELECT @rownum1 := 0) r1, (SELECT @rownum2 := 0) r2
ORDER BY <rank_order_by_field>
;

Harapan itu masuk akal / membantu!

bibzzzz
sumber
-1

Ini Bekerja dengan sempurna bagi saya untuk membuat RowNumber ketika kami memiliki lebih dari satu kolom. Dalam hal ini dua kolom.

SELECT @row_num := IF(@prev_value= concat(`Fk_Business_Unit_Code`,`NetIQ_Job_Code`), @row_num+1, 1) AS RowNumber, 
    `Fk_Business_Unit_Code`,   
    `NetIQ_Job_Code`,  
    `Supervisor_Name`,  
    @prev_value := concat(`Fk_Business_Unit_Code`,`NetIQ_Job_Code`)  
FROM (SELECT DISTINCT `Fk_Business_Unit_Code`,`NetIQ_Job_Code`,`Supervisor_Name`         
      FROM Employee    
      ORDER BY `Fk_Business_Unit_Code`, `NetIQ_Job_Code`, `Supervisor_Name` DESC) z,  
(SELECT @row_num := 1) x,  
(SELECT @prev_value := '') y  
ORDER BY `Fk_Business_Unit_Code`, `NetIQ_Job_Code`,`Supervisor_Name` DESC
ceregala
sumber
-3
set @i = 1;  
INSERT INTO ARG_VALUE_LOOKUP(ARG_VALUE_LOOKUP_ID,ARGUMENT_NAME,VALUE,DESCRIPTION,UPDATE_TIMESTAMP,UPDATE_USER,VER_NBR,OBJ_ID) 
select @i:= @i+1 as ARG_VALUE_LOOKUP_ID,ARGUMENT_NAME,VALUE,DESCRIPTION,CURRENT_TIMESTAMP,'admin',1,UUID() 
FROM TEMP_ARG_VALUE_LOOKUP 
order by ARGUMENT_NAME;
pengguna4605644
sumber
1
Silakan coba memformat jawaban apa pun dan berikan beberapa konteks tambahan tentang apa yang Anda coba lakukan. Saat ini tidak lebih dari teks berformat buruk.
Yannick Meeus
2
Tampaknya ini tidak memiliki hubungan dengan pertanyaan awal. Jika Anda memiliki pertanyaan Anda sendiri, silakan tanyakan secara terpisah.
Jeroen Mostert
-5
SELECT 
    col1, col2, 
    count(*) as intRow
FROM Table1
GROUP BY col1,col2
ORDER BY col3 desc
Nickson Nyabote
sumber