排行榜位置SQL优化

时间:2019-02-08 18:06:24

标签: mysql sql

我正在为Discord机器人提供经验排行榜,该机器人是我积极开发的,例如可以显示个人排名的个人资料卡片。我当前使用的SQL查询可以正常工作,但是我注意到此查询需要相当长的处理时间。

    SELECT id, 
           discord_id, 
           discord_tag, 
           xp, 
           level 
    FROM   (SELECT @rank := @rank + 1 AS id, 
                   discord_id, 
                   discord_tag, 
                   xp, 
                   level 
            FROM   profile_xp, 
                   (SELECT @rank := 0) r 
            ORDER  BY xp DESC) t 
    WHERE  discord_id = '12345678901'; 

该表不是太大(大约20k个唯一记录),但是该查询平均花费300-450ms之间的时间,这在处理大量并发请求时相对较快。

我想知道是否可以优化此查询以提高性能。我已将此查询隔离,MySQL服务器的其余部分响应迅速。

如果有任何提示,我将感到高兴,并在此先感谢! :)

3 个答案:

答案 0 :(得分:1)

不是答案;评论太久了:

我通常会用与您完全相同的方式来写这种东西,因为它既快速又容易,但是这种方法实际上存在技术上的缺陷-尽管它只会在某些情况下变得明显。

通过说明的方式,请考虑以下内容:

DROP TABLE IF EXISTS ints;

CREATE TABLE ints (i INT NOT NULL PRIMARY KEY);

INSERT INTO ints VALUES
(0),(1),(2),(3),(4),(5),(6),(7),(8),(9);

您的查询:

 SELECT a.*
      , @i:=@i+1 rank
   FROM ints a
   JOIN (SELECT @i:=0) vars
  ORDER
     BY RAND() DESC;
    +---+------+
    | i | rank |
    +---+------+
    | 3 |    4 |
    | 2 |    3 |
    | 5 |    6 |
    | 1 |    2 |
    | 7 |    8 |
    | 9 |   10 |
    | 4 |    5 |
    | 6 |    7 |
    | 8 |    9 |
    | 0 |    1 |
    +---+------+

看,结果集根本不是“随机”的。 rank始终对应于i

现在将其与以下内容进行比较:

SELECT a.*
     , @i:=@i+1 rank 
  FROM 
     ( SELECT * FROM ints ORDER by RAND() DESC) a 
  JOIN (SELECT @i:=0) vars;
+---+------+
| i | rank |
+---+------+
| 5 |    1 |
| 2 |    2 |
| 8 |    3 |
| 7 |    4 |
| 4 |    5 |
| 6 |    6 |
| 0 |    7 |
| 1 |    8 |
| 3 |    9 |
| 9 |   10 |
+---+------+

答案 1 :(得分:1)

您要扫描20,000行以分配“行号”,然后从中精确选择一行。您可以改用聚合:

SELECT *, (
    SELECT COUNT(*)
    FROM profile_xp AS x
    WHERE xp > profile_xp.xp
) + 1 AS rnk
FROM profile_xp
WHERE discord_id = '12345678901'

这将为您提供玩家的排名。对于密集排名,请使用COUNT(DISTINCT xp)。如有必要,请在xp列上创建索引。

答案 2 :(得分:0)

假定discord_id是表的主键,而您只是想获得一个条目的“等级”,则应该可以采用其他方法。

SELECT px.discord_id, px.discord_tag, px.xp, px.level
   , 1 + COUNT(leaders.xp) AS rank
   , 1 + COUNT(DISTINCT leaders.xp) AS altRank
FROM profile_xp AS px
LEFT JOIN profile_xp AS leaders ON px.xp < leaders.xp
WHERE px.discord_id = '12345678901'
GROUP BY px.discord_id, px.discord_tag, px.xp, px.level
; 

请注意,我具有“等级”和“ altRank”。 rank应该给您与您最初寻找的位置相似的位置;您的“平局”成绩可能会有所波动,这rank始终会将并列玩家置于最高的“平局”。如果3条记录并列第二,则那些(与此分别查询)将显示第二位,下一个xp向下应该排在第五位(假设1位为1、2、3、4位为2、5位为5)。 altRank将“缩小差距”,将5放在“组”的第三位。

我还建议在xp上加一个索引,以进一步加快速度。