Dapatkan peringkat pengguna di tabel skor

31

Saya memiliki tabel MySQL yang sangat sederhana tempat saya menyimpan skor tinggi. Itu terlihat seperti itu:

Id     Name     Score

Sejauh ini baik. Pertanyaannya adalah: Bagaimana cara saya mendapatkan peringkat pengguna? Sebagai contoh, saya memiliki pengguna Nameatau Iddan ingin mendapatkan peringkatnya, di mana semua baris diperintahkan turun untuk Score.

Sebuah contoh

Id  Name    Score
1   Ida     100
2   Boo     58
3   Lala    88
4   Bash    102
5   Assem   99

Dalam hal ini, Assemperingkatnya adalah 3, karena ia mendapat skor tertinggi ke-3.

Kueri harus mengembalikan satu baris, yang berisi (hanya) Peringkat yang diperlukan.

Michael
sumber

Jawaban:

31
SELECT id, name, score, FIND_IN_SET( score, (
SELECT GROUP_CONCAT( score
ORDER BY score DESC ) 
FROM scores )
) AS rank
FROM scores

berikan daftar ini:

id name  score rank
1  Ida   100   2
2  Boo    58   5
3  Lala   88   4
4  Bash  102   1
5  Assem  99   3

Mendapatkan skor satu orang:

SELECT id, name, score, FIND_IN_SET( score, (    
SELECT GROUP_CONCAT( score
ORDER BY score DESC ) 
FROM scores )
) AS rank
FROM scores
WHERE name =  'Assem'

Memberikan hasil ini:

id name score rank
5 Assem 99 3

Anda akan memiliki satu pemindaian untuk mendapatkan daftar skor, dan pemindaian lainnya atau berusaha melakukan sesuatu yang bermanfaat dengannya. Indeks pada scorekolom akan membantu kinerja pada tabel besar.

cairnz
sumber
3
Korelasi (SELECT GROUP_CONCAT(score) FROM TheWholeTable)bukan cara terbaik. Dan itu mungkin memiliki masalah dengan ukuran baris yang dibuat.
ypercubeᵀᴹ
2
Ini akan gagal jika ada ikatan.
Arvind07
Single permintaan skor orang sangat lambat untuk tabel yang lebih besar .. Sebuah permintaan yang jauh lebih baik untuk menentukan peringkat (dengan kesenjangan untuk ikatan) untuk skor satu orang adalah: SELECT 1 + COUNT(*) AS rank FROM scores WHERE score > (SELECT score FROM scores WHERE name='Assem'). Yang 'hanya' menghitung jumlah entri dengan skor lebih tinggi dari entri saat ini. (Jika Anda menambahkan DISTINCTAnda akan mendapatkan pangkat tanpa celah ..)
Paul
PENTING: GROUP_CONTAT memiliki batas default 1024 karakter, pada set data yang besar itu akan menghasilkan peringkat yang salah, misalnya, itu mungkin berhenti di peringkat 100 dan kemudian melaporkan 0 sebagai peringkat
0plus1
30

Ketika beberapa entri memiliki skor yang sama, peringkat berikutnya tidak boleh berturut-turut. Peringkat berikutnya harus ditambah dengan jumlah skor yang memiliki peringkat yang sama.

Untuk menampilkan skor seperti itu diperlukan dua variabel peringkat

  • variabel peringkat untuk ditampilkan
  • variabel peringkat untuk menghitung

Ini adalah versi peringkat yang lebih stabil dengan ikatan:

SET @rnk=0; SET @rank=0; SET @curscore=0;
SELECT score,ID,rank FROM
(
    SELECT AA.*,BB.ID,
    (@rnk:=@rnk+1) rnk,
    (@rank:=IF(@curscore=score,@rank,@rnk)) rank,
    (@curscore:=score) newscore
    FROM
    (
        SELECT * FROM
        (SELECT COUNT(1) scorecount,score
        FROM scores GROUP BY score
    ) AAA
    ORDER BY score DESC
) AA LEFT JOIN scores BB USING (score)) A;

Mari kita coba ini dengan data sampel. Pertama di sini adalah data sampel:

use test
DROP TABLE IF EXISTS scores;
CREATE TABLE scores
(
    id int not null auto_increment,
    score int not null,
    primary key (id),
    key score (score)
);
INSERT INTO scores (score) VALUES
(50),(40),(75),(80),(55),
(40),(30),(80),(70),(45),
(40),(30),(65),(70),(45),
(55),(45),(83),(85),(60);

Mari memuat data sampel

mysql> DROP TABLE IF EXISTS scores;
Query OK, 0 rows affected (0.15 sec)

mysql> CREATE TABLE scores
    -> (
    ->     id int not null auto_increment,
    ->     score int not null,
    ->     primary key (id),
    ->     key score (score)
    -> );
Query OK, 0 rows affected (0.16 sec)

mysql> INSERT INTO scores (score) VALUES
    -> (50),(40),(75),(80),(55),
    -> (40),(30),(80),(70),(45),
    -> (40),(30),(65),(70),(45),
    -> (55),(45),(83),(85),(60);
Query OK, 20 rows affected (0.04 sec)
Records: 20  Duplicates: 0  Warnings: 0

Selanjutnya, mari inisialisasi variabel pengguna:

mysql> SET @rnk=0; SET @rank=0; SET @curscore=0;
Query OK, 0 rows affected (0.01 sec)

Query OK, 0 rows affected (0.00 sec)

Query OK, 0 rows affected (0.00 sec)

Sekarang, ini adalah output dari query:

mysql> SELECT score,ID,rank FROM
    -> (
    ->     SELECT AA.*,BB.ID,
    ->     (@rnk:=@rnk+1) rnk,
    ->     (@rank:=IF(@curscore=score,@rank,@rnk)) rank,
    ->     (@curscore:=score) newscore
    ->     FROM
    ->     (
    ->         SELECT * FROM
    ->         (SELECT COUNT(1) scorecount,score
    ->         FROM scores GROUP BY score
    ->     ) AAA
    ->     ORDER BY score DESC
    -> ) AA LEFT JOIN scores BB USING (score)) A;
+-------+------+------+
| score | ID   | rank |
+-------+------+------+
|    85 |   19 |    1 |
|    83 |   18 |    2 |
|    80 |    4 |    3 |
|    80 |    8 |    3 |
|    75 |    3 |    5 |
|    70 |    9 |    6 |
|    70 |   14 |    6 |
|    65 |   13 |    8 |
|    60 |   20 |    9 |
|    55 |    5 |   10 |
|    55 |   16 |   10 |
|    50 |    1 |   12 |
|    45 |   10 |   13 |
|    45 |   15 |   13 |
|    45 |   17 |   13 |
|    40 |    2 |   16 |
|    40 |    6 |   16 |
|    40 |   11 |   16 |
|    30 |    7 |   19 |
|    30 |   12 |   19 |
+-------+------+------+
20 rows in set (0.18 sec)

Harap perhatikan bagaimana beberapa ID yang memiliki skor yang sama memiliki peringkat yang sama. Perhatikan juga bahwa peringkat tidak berurutan.

Cobalah !!!

RolandoMySQLDBA
sumber
Karena ini menggunakan variabel cakupan sesi, apakah ini aman jika, katakanlah, beberapa pengguna akhir meminta papan skor pada saat yang sama? Apakah mungkin untuk hasil yang ditetapkan memiliki hasil yang berbeda karena pengguna lain juga menjalankan kueri ini? Bayangkan sebuah API di depan permintaan ini dengan banyak klien yang langsung melakukannya.
Xaero Degreaz
@ XaeroDegreaz Anda benar, itu mungkin. Bayangkan menghitung peringkat untuk suatu game. Satu pengguna bertanya untuk peringkat dan pengguna lain meminta 5 detik setelah seseorang mengalahkan skor tinggi atau memasuki skor X teratas. Meskipun demikian, hal yang sama dapat terjadi jika peringkat dilakukan pada level aplikasi dan bukan pada level server.
RolandoMySQLDBA
Terima kasih balasannya. Kekhawatiran saya tidak benar-benar apakah data bergeser secara organik dari waktu ke waktu, kekhawatiran saya adalah bahwa beberapa pengguna yang melakukan kueri akan memodifikasi / menimpa data yang disimpan dalam variabel lingkup sesi sementara pengguna lain juga melakukan kueri. Apakah itu masuk akal?
Xaero Degreaz
@ XaeroDegreaz itulah keindahan variabel cakupan sesi. Mereka ada di sesi Anda saja, dan tidak ada orang lain. Anda tidak akan melihat variabel sesi dari pengguna lain dan tidak ada yang akan melihat variabel sesi Anda.
RolandoMySQLDBA
Oke, itulah yang saya cenderung percayai - bahwa variabel sesi dibatasi untuk koneksi, dan koneksi tunggal tidak dapat ditempati oleh lebih dari satu orang pada suatu waktu. Setelah koneksi gratis, atau dilemparkan kembali ke kolam, pengguna lain dapat melompat pada koneksi dan variabel sesi diinisialisasi ulang (saat melakukan permintaan ini). Terima kasih lagi untuk informasinya.
Xaero Degreaz
13
SELECT 
    id, 
    Name,
    1+(SELECT count(*) from table_name a WHERE a.Score > b.Score) as RNK,
    Score
FROM table_name b;
a1ex07
sumber
9

Salah satu opsi adalah menggunakan variabel USER:

SET @i=0;
SELECT id, name, score, @i:=@i+1 AS rank 
 FROM ranking 
 ORDER BY score DESC;
Derek Downey
sumber
4

The jawaban yang diterima memiliki masalah potensial. Jika ada dua atau lebih skor identik, akan ada kesenjangan dalam peringkat. Dalam contoh yang dimodifikasi ini:

 id name  score rank
 1  Ida   100   2
 2  Boo    58   5
 3  Lala   99   3
 4  Bash  102   1
 5  Assem  99   3

Skor 58 memiliki peringkat 5, dan tidak ada peringkat 4.

Jika Anda ingin memastikan tidak ada kesenjangan dalam peringkat, gunakan DISTINCTdi GROUP_CONCATuntuk membangun daftar skor yang berbeda:

SELECT id, name, score, FIND_IN_SET( score, (
SELECT GROUP_CONCAT( DISTINCT score
ORDER BY score DESC ) FROM scores)
) AS rank
FROM scores

Hasil:

id name  score rank
1  Ida   100   2
2  Boo    58   4
3  Lala   99   3   
4  Bash  102   1
5  Assem  99   3

Ini juga berfungsi untuk mendapatkan peringkat pengguna tunggal:

SELECT id, name, score, FIND_IN_SET( score, (    
SELECT GROUP_CONCAT(DISTINCT score
ORDER BY score DESC ) 
FROM scores )
) AS rank
FROM scores
WHERE name =  'Boo'

Hasil:

id name score rank
 2  Boo   58    4
Mahesh Lakher
sumber
Permintaan peringkat pengguna tunggal dapat dioptimalkan dengan sangat baik dengan menggunakan COUNTdan subquery sebagai gantinya. Lihat komentar saya di Jawaban Diterima
Paul
Catatan dan peningkatan yang bagus. bekerja dengan sangat baik
SMMousavi
3

Inilah jawaban terbaik:

SELECT 1 + (SELECT count( * ) FROM highscores a WHERE a.score > b.score ) AS rank FROM
highscores b WHERE Name = 'Assem' ORDER BY rank LIMIT 1 ;

Kueri ini akan kembali:

3

FamerJoe
sumber
Saya mengalami masalah kecil dengan pemikiran itu. Sebagai contoh: jika dua pengguna pertama memiliki skor yang berbeda dan sisanya memiliki 0, peringkat untuk orang-orang yang nilainya nol adalah # 4 dan bukan # 3. Tetapi yang pertama benar-benar mendapatkan # 1 dan yang kedua # 2. Ada ide?
fersarr
3

Solusi ini memberikan DENSE_RANKjika ada ikatan:

SELECT *,
IF (@score=s.Score, @rank:=@rank, @rank:=@rank+1) rank,
@score:=s.Score score
FROM scores s,
(SELECT @score:=0, @rank:=0) r
ORDER BY points DESC
Arvind07
sumber
0

Tidakkah pekerjaan berikut ini (dengan asumsi meja Anda disebut Skor)?

SELECT COUNT(id) AS rank FROM Scores 
WHERE score <= (SELECT score FROM Scores WHERE Name = "Assem")
bfredo123
sumber
-4

Saya punya ini, yang memberikan hasil yang sama dengan variabel. Ini bekerja dengan ikatan dan mungkin lebih cepat:

SELECT COUNT(*)+1 as rank
FROM 
(SELECT score FROM scores ORDER BY score) AS sc
WHERE score <
(SELECT score FROM scores WHERE Name="Assem")

Saya tidak mengujinya, tapi saya menggunakan yang berfungsi sempurna, yang saya adaptasi dengan variabel yang Anda gunakan di sini.

Juan
sumber