स्कोर तालिका में उपयोगकर्ता का रैंक प्राप्त करें


31

मेरे पास एक बहुत ही सरल MySQL तालिका है जहां मैं हाईस्कॉर सहेजता हूं। ऐसा दिखता है:

Id     Name     Score

अब तक सब ठीक है। सवाल यह है: मुझे क्या मिलेगा जो एक उपयोगकर्ता रैंक है? उदाहरण के लिए, मेरे पास उपयोगकर्ता हैं Nameया Idउसकी रैंक प्राप्त करना चाहते हैं, जहां सभी पंक्तियों के लिए क्रमबद्ध क्रमबद्ध हैं Score

एक उदाहरण

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

इस मामले में, Assemरैंक 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 y

2
यह संबंधों के मामले में विफल हो जाएगा।
अरविन्द

एकल व्यक्ति स्कोर क्वेरी बड़ी तालिकाओं के लिए बेहद धीमी है .. किसी एकल व्यक्ति के स्कोर के लिए रैंक (संबंधों के लिए अंतराल) के साथ निर्धारित करने के लिए एक बेहतर क्वेरी है 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)

कृपया ध्यान दें कि समान स्कोर साझा करने वाली एकाधिक आईडी में समान रैंक है। यह भी ध्यान दें कि रैंक लगातार नहीं है।

कोशिश तो करो !!!


चूंकि यह सत्र-स्कोप किए गए चर का उपयोग कर रहा है, क्या यह सुरक्षित है यदि, कहते हैं, एक ही समय में कई अंतिम उपयोगकर्ता स्कोरबोर्ड का अनुरोध कर रहे हैं? क्या परिणाम के लिए अलग परिणाम निर्धारित करना संभव है क्योंकि कोई अन्य उपयोगकर्ता भी इस क्वेरी को निष्पादित कर रहा है? इस क्वेरी के सामने एक API की कल्पना करें, जिसमें कई क्लाइंट इसे एक ही बार में हिट करते हैं।
Xaero Degreaz

@XaeroDegreaz आप सही हैं, यह संभव है। एक खेल के लिए रैंक की गणना करने की कल्पना करो। एक उपयोगकर्ता रैंक के लिए और दूसरा उपयोगकर्ता क्वेरी 5 सेकंड के बाद एक व्यक्ति उच्च स्कोर को हराता है या शीर्ष एक्स स्कोर में प्रवेश करता है। इसके बावजूद, यदि सर्वर स्तर के बजाय आवेदन स्तर पर रैंकिंग की गई थी, तो ऐसा ही हो सकता है।
रोलैंडमाइसीडीडीबीए

उत्तर के लिए धन्यवाद। मेरी चिंता वास्तव में यह नहीं है कि डेटा समय-समय पर व्यवस्थित रूप से बदलता है या नहीं, मेरी चिंता यह है कि क्वेरी करने वाले कई उपयोगकर्ता सत्र-स्कोप किए गए चर में संग्रहीत डेटा को संशोधित / अधिलेखित करेंगे, जबकि अन्य उपयोगकर्ता क्वेरी भी कर रहे हैं। क्या इसका कोई मतलब है?
Xaero Degreaz

@XaeroDegreaz कि सत्र गुंजाइश चर की सुंदरता है। वे केवल आपके सत्र में हैं, और किसी और के नहीं। आप अन्य उपयोगकर्ताओं से सत्र चर नहीं देखेंगे और कोई भी आपके सत्र चर नहीं देखेगा।
रोलैंडमाइसीडीडीबीए

ठीक है, यही मैं विश्वास करने के लिए झुकाव की तरह था - उस सत्र चर को कनेक्शन के लिए बंद कर दिया जाता है, और एक बार में एक से अधिक व्यक्तियों द्वारा एकल-कनेक्शन पर कब्जा नहीं किया जा सकता है। एक बार जब कनेक्शन मुफ्त होता है, या पूल में वापस फेंक दिया जाता है, तो एक अन्य उपयोगकर्ता कनेक्शन पर आशा कर सकता है और सत्र चर फिर से आरंभीकृत किया जाता है (जब यह क्वेरी करते हैं)। सूचना के लिए पुनः धन्यवाद।
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;


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 हैं, तो शून्य-स्कोर वाले लोगों की रैंकिंग # 3 के बजाय # 4 है। लेकिन पहला सही ढंग से # 1 और दूसरा # 2 हो रहा है। कोई विचार?
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")

मैंने इसका परीक्षण नहीं किया, लेकिन मैं एक का उपयोग कर रहा हूं जो एकदम सही काम करता है, जिसे मैंने आपके द्वारा यहां उपयोग किए जा रहे चर के साथ अनुकूलित किया है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.