子查询范围忽略MySQL索引

时间:2014-03-02 03:04:04

标签: mysql sql

好的,这是一个关于索引和子查询的简单问题。我正在使用MariaDB 5.5.36 + MyISAM,这是leaderboard的表格结构。它包含大约5000万行,大约2000个级别。

int userid,
int levelid,
int score,

index (userid, levelid),
index (levelid, score)

此查询旨在返回给定用户级别中每个分数的排名,运行速度非常慢......

SELECT levelid, (
    SELECT COUNT(*) + 1
    FROM leaderboard
    WHERE score > l.score AND levelid = l.levelid
) AS rank
FROM leaderboard AS l
WHERE userid = 12345;

我也尝试过使用自联接组方法,这种方法在上面运行的时间只有一半,但仍然慢得令人无法接受:

SELECT x.levelid, COUNT(y.score) AS rank
FROM leaderboard AS x
LEFT JOIN leaderboard AS y ON x.levelid = y.levelid AND y.score > x.score
WHERE x.userid = {0}
GROUP BY x.levelid;

...虽然这个替代方案的运行速度提高了大约100倍(伪代码,在数据库外部的应用程序或存储过程中循环结果,然后使用常量分别运行子查询2000次):

results = execute(""SELECT levelid, score
    FROM leaderboard
    WHERE userid = 12345"");

for each row in results:
    execute(""SELECT COUNT(*) + 1
        FROM leaderboard
        WHERE score > %d AND levelid = %d
    "".printf(row.score, row.levelid));

EXPLAIN告诉我,慢速示例中的子查询有一个4字节的key_len(只是levelid),而快速版本使用8(levelid,score)。有趣的是,如果“score> l.score”被替换为“score = l.score”,它会切换到使用全部8,但显然这并没有给我我正在寻找的答案。

有什么我不了解指数从根本上如何运作?有没有更好的方法来编写这个排名查询?将rank列添加到我的表并在每次达到高分时更新它(这可能意味着更新多达400k行以获得一个单独的分数)会更有效吗?

0 个答案:

没有答案