order by在group by之前的MySQL查询性能改进
下面是我用来根据order by在group by之前的MySQL查询性能改进,mysql,sql,Mysql,Sql,下面是我用来根据serverID获取最新记录的一个查询,不幸的是,这个查询需要无限的时间来处理。根据下面的stackoverflow问题,这应该是一个非常快速的解决方案。是否有任何方法可以加快此查询的速度,或者我必须将其拆分?(首先获取所有服务器ID,然后获取每个服务器的最后一条记录) 样本输出: +------+-------------+-------+------+---------------+------+---------+------+------+----------+----
serverID
获取最新记录的一个查询,不幸的是,这个查询需要无限的时间来处理。根据下面的stackoverflow问题,这应该是一个非常快速的解决方案。是否有任何方法可以加快此查询的速度,或者我必须将其拆分?(首先获取所有服务器ID,然后获取每个服务器的最后一条记录)
样本输出:
+------+-------------+-------+------+---------------+------+---------+------+------+----------+------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+------+-------------+-------+------+---------------+------+---------+------+------+----------+------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE |
+------+-------------+-------+------+---------------+------+---------+------+------+----------+------------------+
1 row in set, 1 warning (0.00 sec)
+-------------+---------------+----------+---------------+-------------------------+--------+
| performance | playersOnline | serverID | name | modpack | color |
+-------------+---------------+----------+---------------+-------------------------+--------+
| 99 | 18 | 15 | hub | Lobby | AAAAAA |
| 98 | 12 | 10 | horizons | Horizons | AA00AA |
| 97 | 6 | 11 | m_lobby | Monster | AA0000 |
| 99 | 1 | 12 | m_north | Monster | AA0000 |
| 86 | 10 | 13 | m_south | Monster | AA0000 |
| 87 | 17 | 14 | m_east | Monster | AA0000 |
| 98 | 10 | 16 | m_west | Monster | AA0000 |
| 84 | 7 | 5 | tppi | Test Pack Please Ignore | 55FFFF |
| 95 | 15 | 6 | agrarian_plus | Agrarian Skies | 00AA00 |
| 98 | 23 | 7 | agrarian2 | Agrarian Skies | 00AA00 |
| 74 | 18 | 9 | agrarian | Agrarian Skies | 00AA00 |
| 97 | 37 | 17 | agrarian3 | Agrarian Skies | 00AA00 |
| 99 | 17 | 3 | bteam_pvp | Attack of the B-Team | FFAA00 |
| 73 | 44 | 8 | bteam_pve | Attack of the B-Team | FFAA00 |
| 93 | 11 | 4 | crackpack | Crackpack | EFEFEF |
+-------------+---------------+----------+---------------+-------------------------+--------+
15 rows in set (38.49 sec)
样本数据:
+------+-------------+-------+------+---------------+------+---------+------+------+----------+------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+------+-------------+-------+------+---------------+------+---------+------+------+----------+------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Impossible WHERE |
+------+-------------+-------+------+---------------+------+---------+------+------+----------+------------------+
1 row in set, 1 warning (0.00 sec)
+-------------+---------------+----------+---------------+-------------------------+--------+
| performance | playersOnline | serverID | name | modpack | color |
+-------------+---------------+----------+---------------+-------------------------+--------+
| 99 | 18 | 15 | hub | Lobby | AAAAAA |
| 98 | 12 | 10 | horizons | Horizons | AA00AA |
| 97 | 6 | 11 | m_lobby | Monster | AA0000 |
| 99 | 1 | 12 | m_north | Monster | AA0000 |
| 86 | 10 | 13 | m_south | Monster | AA0000 |
| 87 | 17 | 14 | m_east | Monster | AA0000 |
| 98 | 10 | 16 | m_west | Monster | AA0000 |
| 84 | 7 | 5 | tppi | Test Pack Please Ignore | 55FFFF |
| 95 | 15 | 6 | agrarian_plus | Agrarian Skies | 00AA00 |
| 98 | 23 | 7 | agrarian2 | Agrarian Skies | 00AA00 |
| 74 | 18 | 9 | agrarian | Agrarian Skies | 00AA00 |
| 97 | 37 | 17 | agrarian3 | Agrarian Skies | 00AA00 |
| 99 | 17 | 3 | bteam_pvp | Attack of the B-Team | FFAA00 |
| 73 | 44 | 8 | bteam_pve | Attack of the B-Team | FFAA00 |
| 93 | 11 | 4 | crackpack | Crackpack | EFEFEF |
+-------------+---------------+----------+---------------+-------------------------+--------+
15 rows in set (38.49 sec)
我将使用
不存在来表达此查询:
SELECT ss.performance, ss.playersOnline, ss.serverID, s.name, m.modpack, m.color
FROM stats_server ss INNER JOIN
server s
ON ss.serverID = s.id INNER JOIN
modpack m
ON s.modpack = m.id
WHERE NOT EXISTS (select 1
from stats_server ss2
where ss2.serverID = ss.serverID AND ss2.id > ss.id
)
除了服务器
和modpack
上的主键索引(我假设它们在那里),您还需要统计服务器(ServerId,id)
上的索引。此索引还应该有助于您的查询版本。我将使用not exists来表达此查询:
SELECT ss.performance, ss.playersOnline, ss.serverID, s.name, m.modpack, m.color
FROM stats_server ss INNER JOIN
server s
ON ss.serverID = s.id INNER JOIN
modpack m
ON s.modpack = m.id
WHERE NOT EXISTS (select 1
from stats_server ss2
where ss2.serverID = ss.serverID AND ss2.id > ss.id
)
除了服务器
和modpack
上的主键索引(我假设它们在那里),您还需要统计服务器(ServerId,id)
上的索引。此索引还应该有助于您的查询版本。我猜您确实需要此索引(请注意连接顺序和连接条件),并且此索引与您创建的索引相匹配:
SELECT s1.performance, s1.playersOnline, s1.serverID, s.name, m.modpack, m.color
FROM server s
INNER JOIN stats_server s1
ON s1.serverID = s.id
LEFT JOIN stats_server s2
ON s2.serverID = s.id AND s2.id > s1.id
INNER JOIN modpack m
ON m.id = s.modpack
WHERE s2.id IS NULL
ORDER BY m.id
MySQL并不总是按照您在查询中写入表的顺序对表进行内部连接,因为顺序对结果集并不重要(尽管它会影响索引的使用)
在WHERE
子句中没有指定可用索引的情况下,MySQL可能希望从行数最少的表开始(在这种情况下可能是stats\u server)。使用orderby
子句,MySQL可能希望从modpack开始,这样以后就不必对结果进行排序
MySQL选择执行计划,然后查看是否有合适的索引用于加入,而不是查看必须加入哪些索引,然后选择执行计划。MySQL不会自动选择与索引匹配的计划
告诉MySQL连接表的顺序,以便使用您希望它使用的索引:
SELECT s1.performance, s1.playersOnline, s1.serverID, s.name, m.modpack, m.color
FROM server s
STRAIGHT_JOIN stats_server s1
ON s1.serverID = s.id
LEFT JOIN stats_server s2
ON s2.serverID = s.id AND s2.id > s1.id
STRAIGHT_JOIN modpack m
ON m.id = s.modpack
WHERE s2.id IS NULL
ORDER BY m.id
我不知道您定义了什么索引,因为您没有提供解释结果或显示索引,但这应该会让您了解如何改善这种情况。我猜您确实想要这样做(注意连接顺序和连接条件),并且这与您创建的索引相匹配:
SELECT s1.performance, s1.playersOnline, s1.serverID, s.name, m.modpack, m.color
FROM server s
INNER JOIN stats_server s1
ON s1.serverID = s.id
LEFT JOIN stats_server s2
ON s2.serverID = s.id AND s2.id > s1.id
INNER JOIN modpack m
ON m.id = s.modpack
WHERE s2.id IS NULL
ORDER BY m.id
MySQL并不总是按照您在查询中写入表的顺序对表进行内部连接,因为顺序对结果集并不重要(尽管它会影响索引的使用)
在WHERE
子句中没有指定可用索引的情况下,MySQL可能希望从行数最少的表开始(在这种情况下可能是stats\u server)。使用orderby
子句,MySQL可能希望从modpack开始,这样以后就不必对结果进行排序
MySQL选择执行计划,然后查看是否有合适的索引用于加入,而不是查看必须加入哪些索引,然后选择执行计划。MySQL不会自动选择与索引匹配的计划
告诉MySQL连接表的顺序,以便使用您希望它使用的索引:
SELECT s1.performance, s1.playersOnline, s1.serverID, s.name, m.modpack, m.color
FROM server s
STRAIGHT_JOIN stats_server s1
ON s1.serverID = s.id
LEFT JOIN stats_server s2
ON s2.serverID = s.id AND s2.id > s1.id
STRAIGHT_JOIN modpack m
ON m.id = s.modpack
WHERE s2.id IS NULL
ORDER BY m.id
我不知道您定义了哪些索引,因为您没有提供解释结果或显示索引,但这应该会让您了解如何改善这种情况。编辑
好的,我解决了。以下是显示原始慢速查询的展开行:
这里是一个使用MAX()
和groupby
的快速查询,它给出了相同的结果。请自己试一试
SELECT s1.id
,s1.performance
,s1.playersOnline
,s1.serverID
,s.name
,m.modpack
,m.color
FROM stats_server s1
JOIN (
SELECT MAX(id) as 'id'
FROM stats_server
GROUP BY serverID
) AS s2
ON s1.id = s2.id
JOIN server s
ON s1.serverID = s.id
JOIN modpack m
ON s.modpack = m.id
ORDER BY m.id
编辑
好的,我解决了。以下是显示原始慢速查询的展开行:
这里是一个使用MAX()
和groupby
的快速查询,它给出了相同的结果。请自己试一试
SELECT s1.id
,s1.performance
,s1.playersOnline
,s1.serverID
,s.name
,m.modpack
,m.color
FROM stats_server s1
JOIN (
SELECT MAX(id) as 'id'
FROM stats_server
GROUP BY serverID
) AS s2
ON s1.id = s2.id
JOIN server s
ON s1.serverID = s.id
JOIN modpack m
ON s.modpack = m.id
ORDER BY m.id
我是不是遗漏了什么?为什么标准的不相关子查询不起作用
SELECT x.id, x.performance, x.playersOnline, s.name, m.modpack, m.color, x.timestamp
FROM stats_server x
JOIN
( SELECT serverid, MAX(id) maxid FROM stats_server GROUP BY serverid ) y
ON y.serverid = x.serverid AND y.maxid = x.id
JOIN server s
ON x.serverID=s.id
JOIN modpack m
ON s.modpack=m.id
我错过什么了吗?为什么标准的不相关子查询不起作用
SELECT x.id, x.performance, x.playersOnline, s.name, m.modpack, m.color, x.timestamp
FROM stats_server x
JOIN
( SELECT serverid, MAX(id) maxid FROM stats_server GROUP BY serverid ) y
ON y.serverid = x.serverid AND y.maxid = x.id
JOIN server s
ON x.serverID=s.id
JOIN modpack m
ON s.modpack=m.id
发布查询的解释计划explain SELECT s1.performance,s1.playersOnline,…。
@MKhalidJunaid kay,addedI我真的不明白为什么需要将stats_服务器表连接到自身。您没有引用任何列,当WHERE为NULL时,应该不需要小于限定符。尝试删除s2连接,使其中s1.id为NULL,并查看是否返回相同的数据我不确定mysql,但在其他数据库中,“WHERE s2.id为NULL”会将左侧连接更改为内部连接。这是使用Gordon方法的另一个原因。@user2693017,您在其中添加了解释计划?查询的解释计划后explain SELECT s1.performance,s1.playersOnline,…。
@MKhalidJunaid kay,addedI真的不明白为什么需要将stats_服务器表连接到它本身。您没有引用任何列,当WHERE为NULL时,应该不需要小于限定符。尝试删除s2连接,使其中s1.id为NULL,并查看是否返回相同的数据我不确定mysql,但在其他数据库中,“WHERE s2.id为NULL”会将左侧连接更改为内部连接。这是使用Gordon方法的另一个原因。@user2693017,您在其中添加了解释计划?它们已经被索引了。我很难理解您的查询,我从未在不存在的地方使用过,而且它不会返回任何结果。我应该导出一些测试数据吗?我同意索引(仍然取决于MySQL执行计划),但我不认为WHERE NOT EXISTS
将如何改进此索引,因为它是一个依赖子查询。@MarcusAdams NOT every time EXISTS比left join快得多空,但有时它接管连接时经过了大量优化@user2693017。当你说“已编制索引”时,你的意思是有一个综合索引吗