我有一个游戏应用程序,其中用户用户回答问题,评级基于回答此问题所用的时间。
我正在尝试构建一个返回前20名玩家评级的查询。游戏有一些阶段,我需要检索播放所有阶段的玩家(假设阶段的数量是5)
这就是我写的:
SELECT `usersname` , `time`
FROM `users`
WHERE `users`.`id`
IN (
SELECT `steps`.`user_id`
FROM `steps`
GROUP BY `steps`.`user_id`
HAVING COUNT( `steps`.`id` ) = 5
)
ORDER BY `time` ASC
LIMIT 20
在内部选择中我选择了已经玩过5个阶段(步骤)的所有user_id
。查询工作正常,但速度非常慢。执行需要大约一分半钟。你能提供一些优化技巧吗?内部选择返回约2000行。
如果您需要其他信息,请随时问我。
答案 0 :(得分:2)
假设你有一个关于users.time的索引,这是第一个明显的优化,在内部查询中用WHERE替换HAVING可能值得一试。
如果幸运的话,查询优化器可能已经这样做了,但你不能依赖它,严格按照规范,HAVING在获取每条记录后运行,而WHERE在之前修剪它们
如果这没有帮助,只需为在users表中完成的每个阶段增加一个计数器可能会加快速度,从而消除子查询。这将使完成一个阶段的速度变得极慢(但这不会每秒发生一百万次!),但只能查询已经完成所有5个阶段的用户(特别是如果你有该字段的索引)
此外,使用memcached或类似的缓存技术可能值得像高分一样,这通常是“不一定100%准确到第二,变化缓慢,数十亿次查询”数据。
如果memcached不是一个选项,即使将结果写入临时文件并重新使用它1-2秒(甚至更长)也是一种选择。没有人会注意到。即使您将高分数缓存长达1-2分钟,仍然没有人会冒犯,因为这只是“需要多长时间”。
答案 1 :(得分:2)
尝试使用JOIN
,而不是IN (SELECT ...)
:
SELECT usersname , `time`
FROM users
JOIN
( SELECT steps.user_id
FROM steps
GROUP BY steps.user_id
HAVING COUNT(*) = 5
) grp
ON grp.user_id = users.id
ORDER BY `time` ASC
LIMIT 20
答案 2 :(得分:0)
我认为你应该使用而不是拥有。此外,在我看来,你应该在存储函数中执行此操作。在我看来,最好的方法是使用where而不是运行内部查询,存储结果并根据内部查询的结果运行外部查询。
答案 3 :(得分:0)
此用例可能会从非规范化中受益。无需搜索所有2000个用户记录来确定用户是否优于20个记录。
你可以做的事情。
由于Top_20_Users表格太小,因此为舞台添加一个字段,并为每个阶段以及所有五个完成阶段包括前20个。
让Top_20_Users表增长。有史以来所有前20名用户的历史,他们的时间和时间足以成为前20名的日期。当用户了解游戏并且前20次变得越来越好时,显示趋势。