我正在为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服务器的其余部分响应迅速。
如果有任何提示,我将感到高兴,并在此先感谢! :)
答案 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
上加一个索引,以进一步加快速度。