Puan tablosunda bir kullanıcının puanını al


31

Yüksek puanları biriktirdiğim çok basit bir MySQL masam var. Öyle görünüyor:

Id     Name     Score

Çok uzak çok iyi. Soru şudur: Bir kullanıcının rütbesi nedir? Örneğin, bir kullanıcı var Nameveya Idtüm sıralarının sırasına inme emri vereceği rütbesini almak istiyorum Score.

Bir örnek

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

Bu durumda, Assemrütbe 3, çünkü en yüksek 3. sırayı aldı.

Sorgu, yalnızca (gerekli) Sıralamayı içeren bir satır döndürmelidir.

Yanıtlar:


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

bu listeyi verir:

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

Tek kişilik puan almak:

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'

Bu sonucu verir:

id name score rank
5 Assem 99 3

Puan listesini almak için bir tarama yapacak, bir başka tarama yapacak ya da onunla faydalı bir şeyler yapmaya çalışacaksınız. scoreSütundaki bir dizin, büyük tablolarda performansa yardımcı olur.


3
Korelasyon (SELECT GROUP_CONCAT(score) FROM TheWholeTable)en iyi yol değildir. Ve oluşturulan satırın boyutu ile ilgili bir sorun olabilir.
ypercubeᵀᴹ

2
Bağ durumunda bu başarısız olur.
Arvind07

Tek kişi skor sorgu son derece yavaş daha büyük tablolar .. Bir çok daha iyi sorgu için (ilişkilerden sorumlu boşlukları ile) sırasını belirlemekte tek kişinin puanı içindir geçerli: SELECT 1 + COUNT(*) AS rank FROM scores WHERE score > (SELECT score FROM scores WHERE name='Assem'). Hangi 'sadece' geçerli girdiden daha yüksek bir puana sahip girişlerin sayısını sayar. ( DISTINCTEklerseniz boşluksuz rütbeyi alacaksınız ..)
Paul,

ÖNEMLİ: GROUP_CONTAT varsayılan olarak 1024 karakterlik bir limite sahiptir, büyük veri kümelerinde yanlış sonuçlara neden olur, örneğin 100. sırada durabilir ve sonra 0 olarak sıralama olarak rapor edebilir
0plus1

30

Birden fazla giriş aynı puana sahipse, bir sonraki sıralama ardışık olmamalıdır. Bir sonraki sıralama aynı sırayı paylaşan puanların sayısı ile artırılmalıdır.

Bunun gibi puanları görüntülemek için iki sıra değişkeni gerekir

  • gösterilecek sıralama değişkeni
  • hesaplamak için rütbe değişkeni

İşte bağları olan sıralamanın daha kararlı bir versiyonu:

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;

Bunu örnek verilerle deneyelim. İlk İşte örnek veri:

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

Örnek verileri yükleyelim

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

Daha sonra kullanıcı değişkenlerini başlatalım:

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)

Şimdi, sorgunun çıktısı:

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)

Lütfen aynı puanı paylaşan birden fazla ID'nin aynı sıralamaya sahip olduğunu unutmayın. Ayrıca, rütbe ardışık olmadığını unutmayın.

Bir şans ver !!!


Bu, oturum kapsamındaki değişkenleri kullandığından, aynı anda birden fazla son kullanıcı çetele istediğinde, bu güvenli midir? Başka bir kullanıcı da bu sorguyu yürüttüğü için sonucun farklı sonuçlara ulaşması mümkün mü? Birçok müşterinin bir kerede vurması ile bu sorgunun önündeki bir API'yi hayal edin.
Xaero Degreaz

@XaeroDegreaz Haklısınız, mümkün. Bir oyun için hesaplama aşamalarını düşünün. Bir kullanıcı rütbe için sorgularken, başka bir kullanıcı bir kişi yüksek puanı geçtikten veya en iyi X puanına girdikten 5 saniye sonra sorgular. Bununla birlikte, sıralama sunucu seviyesinden ziyade uygulama düzeyinde yapılıyorsa aynı şey olabilir.
RolandoMySQLDBA

Yanıt için teşekkürler. Benim endişem gerçekten verinin zaman içinde organik olarak değişip değişmeyeceği değil, benim endişem sorguyu gerçekleştiren birden fazla kullanıcının oturum kapsamındaki değişkenlerde depolanan verileri değiştirirken / üzerine yazması ve diğer kullanıcılar da sorguyu gerçekleştirmesi. bu mantıklı mı?
Xaero Degreaz

@XaeroDegreaz, oturum kapsamı değişkenlerinin güzelliğidir. Sadece sizin oturumunuzdalar ve başka hiç kimse yok. Diğer kullanıcılardan gelen oturum değişkenlerini göremezsiniz ve oturum değişkenlerinizi kimse görmez.
RolandoMySQLDBA

Tamam, inanmaya eğilimli olduğum şey buydu - oturum değişkenleri bağlantıya dahil edildi ve tek bir bağlantı bir kerede birden fazla kişi tarafından yapılamaz. Bağlantı serbest kaldığında veya tekrar havuza atıldığında, başka bir kullanıcı bağlantıya atlayabilir ve oturum değişkenleri yeniden başlatılır (bu sorguyu uygularken). Bilgi için tekrar teşekkürler.
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;

9

Bir seçenek, USER değişkenlerini kullanmak olacaktır:

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

4

Kabul edilen cevabın potansiyel bir sorunu var. İki veya daha fazla aynı puan varsa, sıralamada boşluklar olacaktır. Bu değiştirilmiş örnekte:

 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. sırada ve 4. sırada yok.

Yapmak isterseniz emin sıralamasında herhangi bir boşluk, kullanım vardır DISTINCTiçinde GROUP_CONCATayrı puanlarının bir liste oluşturmak için:

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

Sonuç:

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

Bu aynı zamanda tek bir kullanıcının rütbesini almak için de çalışır:

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'

Sonuç:

id name score rank
 2  Boo   58    4

Tek bir kullanıcının rütbe sorgusu, COUNTbunun yerine ve bir alt sorgu kullanılarak büyük ölçüde optimize edilebilir . Kabul Edilmiş
Paul

İyi not ve geliştirme. çok iyi çalışıyor
SMMousavi

3

İşte en iyi cevap:

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 ;

Bu sorgu dönecek:

3


Düşündüğümde ufak bir sorun yaşıyorum. Örneğin: ilk iki kullanıcı farklı puanlara sahipse ve geri kalan her şey 0 ise, sıfır puan alan kişilerin sıralamaları # 3 yerine # 4'tür. Fakat ilki doğru # 1 ve ikinci # 2 oluyor. Herhangi bir fikir?
fersarr

3

Bu çözüm DENSE_RANKbağlar durumunda verir :

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

Aşağıdaki işler işe yaramaz mı (masanızın Puanlar olduğunu varsayarsak)?

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

-4

Değişkenler ile aynı sonuçları veren buna sahibim. Bağları ile çalışır ve daha hızlı olabilir:

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

Test etmedim, fakat mükemmel çalışan birini kullanıyorum, ki burada kullandığınız değişkenlerle buna adapte oldum.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.