mysql - 使用 MAX() 优化 MYSQL 查询

标签 mysql join query-optimization max phpactiverecord

我有一个需要优化的 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_usersleaderboard_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/

相关文章:

mysql - 可以使用索引的 Find_in_set 的替代方案

mysql - Joomla安装错误INSTL_ERROR_INITIALISE_SCHEMA

php - mysql_fetch_array()/mysql_fetch_assoc()/mysql_fetch_row()/mysql_num_rows等…期望参数1为资源或结果

mysql - 连接两个表并在mysql上显示不同的列

MySQL:多对多如何获取所有(相关)类别和组合查询

mysql - MySQL 或 PostgreSQL 的汉明距离优化?

mysql - mysql中的语法错误?

动态更改其中的表时,Mysql调用过程失败

java - 如何使用 JDBI SQL 对象 API 创建一对多关系?

postgresql - 为什么 Postgres 优化器切换到嵌套循环进行连接?