รับอันดับของผู้ใช้ในตารางคะแนน


31

ฉันมีตาราง MySQL ง่าย ๆ ที่ฉันบันทึกคะแนนสูงสุด ดูเหมือนว่า:

Id     Name     Score

จนถึงตอนนี้ดีมาก คำถามคือฉันจะได้รับสิ่งที่ผู้ใช้อันดับได้อย่างไร ตัวอย่างเช่นผมมีผู้ใช้Nameหรือและต้องการที่จะได้รับตำแหน่งของเขาซึ่งแถวทั้งหมดเป็นลำดับจากมากไปน้อยสั่งสำหรับIdScore

ตัวอย่าง

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

ในกรณีนี้Assemอันดับของเขาจะเป็น 3 เพราะเขาได้คะแนนสูงสุดอันดับที่ 3

แบบสอบถามควรส่งคืนหนึ่งแถวซึ่งมีอันดับที่จำเป็น (เท่านั้น)

คำตอบ:


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

ให้รายการนี้:

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

รับคะแนนคนเดียว:

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'

ให้ผลลัพธ์นี้:

id name score rank
5 Assem 99 3

คุณจะมีการสแกนหนึ่งครั้งเพื่อรับรายการคะแนนและอีกการสแกนหรือพยายามทำสิ่งที่มีประโยชน์กับมัน ดัชนีในscoreคอลัมน์จะช่วยให้ประสิทธิภาพการทำงานในตารางขนาดใหญ่


3
ความสัมพันธ์(SELECT GROUP_CONCAT(score) FROM TheWholeTable)ไม่ใช่วิธีที่ดีที่สุด และอาจมีปัญหากับขนาดของแถวที่สร้างขึ้น
ypercubeᵀᴹ

2
สิ่งนี้จะล้มเหลวในกรณีของความสัมพันธ์
Arvind07

แบบสอบถามคะแนนคนเดียวเป็นอย่างมากช้าสำหรับตารางขนาดใหญ่ .. แบบสอบถามที่ดีมากในการกำหนดตำแหน่ง (มีช่องว่างสำหรับความสัมพันธ์) SELECT 1 + COUNT(*) AS rank FROM scores WHERE score > (SELECT score FROM scores WHERE name='Assem')สำหรับคะแนนที่คนเพียงคนเดียวคือ: ซึ่ง 'เพิ่ง' จะนับจำนวนรายการที่มีคะแนนสูงกว่ารายการปัจจุบัน (ถ้าคุณเพิ่มDISTINCTคุณจะได้รับอันดับโดยไม่มีช่องว่าง .. )
พอล

สำคัญ: GROUP_CONTAT มีขีด จำกัด เริ่มต้นที่ 1024 อักขระในชุดข้อมูลขนาดใหญ่ซึ่งจะส่งผลให้อันดับไม่ถูกต้องตัวอย่างเช่นอาจหยุดที่อันดับ 100 จากนั้นรายงาน 0 เป็นอันดับ
0plus1

30

เมื่อหลายรายการมีคะแนนเท่ากันอันดับต่อไปไม่ควรต่อเนื่องกัน อันดับถัดไปควรเพิ่มขึ้นตามจำนวนคะแนนที่ใช้อันดับเดียวกัน

ในการแสดงคะแนนเช่นนั้นต้องใช้ตัวแปรอันดับสอง

  • จัดอันดับตัวแปรที่จะแสดง
  • ตัวแปรอันดับในการคำนวณ

นี่คือการจัดอันดับเวอร์ชันที่เสถียรยิ่งขึ้นด้วยความสัมพันธ์:

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;

ลองทำตัวอย่างนี้กับข้อมูลตัวอย่าง แรกนี่คือข้อมูลตัวอย่าง:

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);

ลองโหลดข้อมูลตัวอย่าง

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

ถัดไปให้เริ่มต้นตัวแปรผู้ใช้:

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)

ตอนนี้นี่คือผลลัพธ์ของแบบสอบถาม:

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)

โปรดทราบว่าหลาย ID ที่ใช้คะแนนเดียวกันมีอันดับเดียวกัน โปรดทราบว่าอันดับนั้นไม่ติดกัน

ให้มันลอง !!!


เนื่องจากสิ่งนี้กำลังใช้ตัวแปรที่มีการกำหนดขอบเขตเซสชันสิ่งนี้ปลอดภัยหรือไม่ถ้าผู้ใช้หลายคนร้องขอกระดานคะแนนพร้อมกัน เป็นไปได้ไหมที่ชุดผลลัพธ์จะมีผลลัพธ์ที่แตกต่างกันเนื่องจากผู้ใช้รายอื่นกำลังเรียกใช้แบบสอบถามนี้ด้วย ลองนึกภาพ API ที่อยู่ด้านหน้าของเคียวรีนี้กับไคลเอนต์จำนวนมากที่กดปุ่มพร้อมกัน
Xaero Degreaz

@XaeroDegreaz คุณพูดถูกมันเป็นไปได้ ลองนึกภาพการคำนวณอันดับสำหรับเกม ผู้ใช้หนึ่งคนถามหาอันดับและผู้ใช้คนอื่นถาม 5 วินาทีหลังจากที่คนเต้นคะแนนสูงหรือเข้าสู่คะแนน X อันดับสูงสุด อย่างไรก็ตามอาจเกิดขึ้นได้หากมีการจัดอันดับในระดับแอปพลิเคชันมากกว่าระดับเซิร์ฟเวอร์
RolandoMySQLDBA

ขอบคุณสำหรับการตอบกลับ. ความกังวลของฉันไม่ได้เป็นจริงหรือไม่ว่าข้อมูลเลื่อนตามเวลาจริงหรือไม่ความกังวลของฉันคือผู้ใช้หลายคนที่ทำแบบสอบถามจะแก้ไข / เขียนทับข้อมูลที่เก็บไว้ในตัวแปรที่มีการกำหนดขอบเขตเซสชันในขณะที่ผู้ใช้คนอื่น ๆ มันสมเหตุสมผลไหม
Xaero Degreaz

@XaeroDegreaz นั่นคือความงามของตัวแปรขอบเขตเซสชัน พวกเขาอยู่ในเซสชั่นของคุณเท่านั้นและไม่มีใครอื่น คุณจะไม่เห็นตัวแปรเซสชันจากผู้ใช้รายอื่นและจะไม่มีใครเห็นตัวแปรเซสชันของคุณ
RolandoMySQLDBA

โอเคนั่นคือสิ่งที่ฉันโน้มตัวไปสู่ความเชื่อว่าตัวแปรเซสชั่นถูกกำหนดขอบเขตให้กับการเชื่อมต่อและการเชื่อมต่อเดียวไม่สามารถครอบครองได้มากกว่าหนึ่งคนในเวลาเดียวกัน เมื่อการเชื่อมต่อนั้นว่างหรือถูกโยนกลับเข้าไปในกลุ่มผู้ใช้รายอื่นสามารถข้ามไปที่การเชื่อมต่อและตัวแปรเซสชันจะเริ่มต้นใหม่ได้ (เมื่อดำเนินการกับแบบสอบถามนี้) ขอบคุณอีกครั้งสำหรับข้อมูล
Xaero Degreaz



4

คำตอบที่ได้รับการยอมรับมีปัญหาที่อาจเกิดขึ้น หากมีคะแนนเท่ากันสองคะแนนขึ้นไปจะมีช่องว่างในการจัดอันดับ ในตัวอย่างที่แก้ไขนี้:

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

คะแนน 58 มีอันดับ 5 และไม่มีอันดับ 4

หากคุณต้องการให้แน่ใจว่ามีช่องว่างในการจัดอันดับไม่มีการใช้DISTINCTในGROUP_CONCATการสร้างรายชื่อของคะแนนที่แตกต่างกัน:

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

ผล:

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

วิธีนี้ใช้สำหรับการจัดอันดับผู้ใช้คนเดียว:

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'

ผล:

id name score rank
 2  Boo   58    4

แบบสอบถามอันดับของผู้ใช้คนเดียวสามารถปรับให้เหมาะสมอย่างมากโดยใช้COUNTและแบบสอบถามย่อยแทน ดูความคิดเห็นของฉันที่คำตอบที่ยอมรับ
พอล

ทราบดีและการปรับปรุง ทำงานได้ดีมาก
SMMousavi

3

นี่คือคำตอบที่ดีที่สุด:

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 ;

แบบสอบถามนี้จะส่งคืน:

3


ฉันมีปัญหาเล็กน้อยกับความคิด ตัวอย่างเช่น: หากผู้ใช้สองคนแรกมีคะแนนแตกต่างกันและที่เหลือทั้งหมดมี 0 การจัดอันดับสำหรับคนที่มีคะแนนศูนย์คือ # 4 แทนที่จะเป็น # 3 แต่สิ่งแรกคือการได้รับ # 1 อย่างถูกต้องและอันดับที่สอง ความคิดใด ๆ
fersarr

3

วิธีแก้ปัญหานี้ให้DENSE_RANKในกรณีของความสัมพันธ์:

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

0

งานต่อไปนี้จะไม่ทำงาน (สมมติว่าตารางของคุณเรียกว่าคะแนน)

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

-4

ฉันมีสิ่งนี้ซึ่งให้ผลลัพธ์เช่นเดียวกับตัวแปร ใช้งานได้กับความสัมพันธ์และอาจเร็วกว่า:

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

ฉันไม่ได้ทดสอบ แต่ฉันใช้อันที่สมบูรณ์แบบซึ่งฉันปรับให้เข้ากับสิ่งที่คุณใช้ที่นี่

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.