我有一个需要优化的 MYSQL 查询,它在我的测试环境中运行得很好,但是对于较大的数据库,它的速度很慢
我使用 PHP activerecord 作为我的数据库处理程序。
Users:
userId | userName | gameId
-------+----------+--------
1 | John | 1
2 | Sally | 1
3 | Mike | 2
4 | Lex | 1
Scores:
id | userId | gameId | score | added |
---+--------+---------+-------+-----------+
1 | 2 | 1 | 300 | time
2 | 2 | 1 | 325 |
3 | 1 | 1 | 200 |
4 | 1 | 1 | 400 |
5 | 4 | 1 | 100 |
extra_fields:
id | score_id | fieldname | fieldvalue |
---+----------+-----------+------------+
1 | 1 | level | 5 |
2 | 1 | image | icon.jpg |
3 | 2 | level | 7 |
4 | 2 | image | smilie.jpg |
5 | 3 | level | 5 |
6 | 3 | image | hello.jpg |
7 | 4 | level | 1 |
8 | 4 | image | fun.png |
9 | 5 | level | 3 |
10 | 5 | image | mfw.png |
现在问题来了,我想从每个用户中选择最高分,然后获取相关的额外值。 因此,在上面的示例数据库中,结果将如下所示:
游戏 1 中的用户请求(其中 gameId = 1):
1 -> username: John ; Score: 400 ; level : 1 ; image : fun.png
2 -> username: Sally ; Score: 325 ; level : 7 ; image : smilie.jpg
3 -> username: Lex ; Score: 100 ; level 3 ; image : mfw.png
现在这就是我所拥有的:
"SELECT * FROM leaderboard_users a JOIN (
SELECT d1.*
FROM leaderboard_scores d1
LEFT OUTER JOIN leaderboard_scores d2
ON (d1.userId = d2.userId AND d1.score < d2.score AND d1.added < d2.added)
WHERE d2.id is null AND d1.gameId = " . intval($this->gameId) . "
AND DATEDIFF(NOW() , d1.added) <= " . intval($this->calcPeriod) . "
)b
ON b.userId = a.userId
GROUP BY b.userId
ORDER BY b.score DESC
LIMIT " . $this->limitWithOffset . " , " . $this->limit;
从中我获得用户名、分数和score_id,然后我进行另一个查询以查找所有额外字段(如果有)
$extraValues = \extraFields::find('all', array(
'conditions' => array(
'score_id = ?',
$j->id)
));
我猜测 JOIN 语句需要时间,因为我连接了分数表中的所有记录(30k+),这看起来很疯狂。
有谁知道我该如何优化它? 或者我的表格布局完全错误,需要更改?
编辑(为 RaviH 解释)
id select_type table type possible_keys key key_len ref rows Extra
1 PRIMARY <derived2> ALL NULL NULL NULL NULL 1554 Using temporary; Using filesort
1 PRIMARY a eq_ref PRIMARY PRIMARY 4 b.userId 1
2 DERIVED d1 ALL NULL NULL NULL NULL 41644 Using where
2 DERIVED d2 ref leaderboard_scores_FI_1 leaderboard_scores_FI_1 4 lechuck_se.d1.userId 12 Using where; Not exists
最佳答案
您的查询正在从 leaderboard_users
和 leaderboard_scores
表中获取所有行,从而导致用户表和分数自连接结果之间出现交叉连接。这些交叉连接的临时结果是巨大的。因此它已经放慢了。随着更多行数添加到用户和分数表中,它会变得更慢。
尝试下面的查询:
"SELECT * FROM leaderboard_users u JOIN (
SELECT userId, MAX(score) FROM leaderboard_scores
WHERE gameId=" . intval($this->gameId) . " AND DATEDIFF(NOW(), added) <= " . intval($this->calcPeriod) . " GROUP BY userId) s
ON u.userId = s.userId"
如果能以某种方式避免动态计算的DATEDIFF
,查询速度可以进一步提高。我无法为此提供通用解决方案,因为这取决于您的要求和数据库设计。
希望这有帮助!
关于mysql - 使用 MAX() 优化 MYSQL 查询,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/21506249/