MySQL性能,当查询使用order by/group by时,子查询使用临时文件排序
在为用户创建的游戏地图存档制作标记表时,用于获取包含所有提供标记的地图的地图ID的SQL是,带有。。。作为标签和#作为标签的数量:MySQL性能,当查询使用order by/group by时,子查询使用临时文件排序,mysql,Mysql,在为用户创建的游戏地图存档制作标记表时,用于获取包含所有提供标记的地图的地图ID的SQL是,带有。。。作为标签和#作为标签的数量: SELECT DISTINCT map_id FROM `map_tag` INNER JOIN `tag` USING (tag_id) WHERE tag IN (...) GROUP BY map_id HAVING COUNT(DISTINCT tag_id) = # ORDER BY map_id DESC /* Affected rows: 0
SELECT DISTINCT map_id
FROM `map_tag`
INNER JOIN `tag` USING (tag_id)
WHERE tag IN (...)
GROUP BY map_id HAVING COUNT(DISTINCT tag_id) = #
ORDER BY map_id DESC
/* Affected rows: 0 Found rows: 83,597 Warnings: 0 Duration for 1 query: 0.032 sec. (+ 0.531 sec. network) */
+----+-------------+---------+-------+---------------+---------+---------+-------+--------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+---------+-------+---------------+---------+---------+-------+--------+--------------------------+
| 1 | SIMPLE | tag | const | PRIMARY,tag | tag | 767 | const | 1 | Using index |
| 1 | SIMPLE | map_tag | index | NULL | PRIMARY | 8 | NULL | 888729 | Using where; Using index |
+----+-------------+---------+-------+---------------+---------+---------+-------+--------+--------------------------+
然后我加入映射本身,SQL变成:
SELECT
`map`.*
FROM (
SELECT DISTINCT map_id
FROM `map_tag`
INNER JOIN `tag` USING (tag_id)
WHERE tag IN (...)
GROUP BY map_id HAVING COUNT(DISTINCT tag_id) = #
ORDER BY map_id DESC
) matching
INNER JOIN `map` USING (map_id)
INNER JOIN `map_tag` USING (map_id)
INNER JOIN `tag` USING (tag_id)
LIMIT 0, 10
/* Affected rows: 0 Found rows: 10 Warnings: 0 Duration for 1 query: 0.297 sec. */
+----+-------------+------------+--------+---------------+---------+---------+---------------------------+--------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+------------+--------+---------------+---------+---------+---------------------------+--------+--------------------------+
| 1 | PRIMARY | <derived2> | ALL | NULL | NULL | NULL | NULL | 83597 | |
| 1 | PRIMARY | map | eq_ref | PRIMARY | PRIMARY | 4 | matching.map_id | 1 | |
| 1 | PRIMARY | map_tag | ref | PRIMARY | PRIMARY | 4 | matching.map_id | 2 | Using index |
| 1 | PRIMARY | tag | eq_ref | PRIMARY | PRIMARY | 4 | maps.local.map_tag.tag_id | 1 | Using index |
| 2 | DERIVED | tag | const | PRIMARY,tag | tag | 767 | | 1 | Using index |
| 2 | DERIVED | map_tag | index | NULL | PRIMARY | 8 | NULL | 888729 | Using where; Using index |
+----+-------------+------------+--------+---------------+---------+---------+---------------------------+--------+--------------------------+
如果我搜索标签“bbb”和“ccc”,结果是:
+-------+---------------+
| name | tags |
+-------+---------------+
| map A | aaa, bbb, ccc |
| map B | bbb, ccc, zzz |
+-------+---------------+
所有标记都属于每个映射,而不仅仅是匹配的标记,并且我能够按map
列对结果的map
行进行排序,而MySQL不会忽略索引:
...
ORDER BY `map`.published DESC
/* Affected rows: 0 Found rows: 10 Warnings: 0 Duration for 1 query: 00:01:35 (+ 0.078 sec. network) */
然而,并没有真正理解你的问题,也没有回答你的评论。。。我会尝试用这种方式来组织它。。。您的内部查询是一个来自map_标记和符合条件的标记上的标记表的联接,distinct的组concat在那里完成,其计数按map id分组。完成。。。现在,您可以只加入到符合条件的映射表中 为了帮助索引优化,我可以建议以下索引
table index
map_tag ( map_id, tag_id )
tag ( tag_id, tag )
map ( map_id )
SELECT
m.*,
PreTags.allTags
from
( SELECT
mt.map_id,
GROUP_CONCAT(DISTINCT t.tag ORDER BY t.tag SEPARATOR ',') allTags
FROM
map_tag mt
JOIN `tag` t
ON mt.tag_id = t.tag_id
group by
mt.map_id
having
SUM( case when t.tag in (...) then 1 else 0 end ) > 1
order by
mt.map_id DESC ) PreTags
JOIN map m
ON PreTags.map_id = m.map_id
limit
0, 10
这样,内部查询为您和用户执行组concat,这样您就不必在获取最终地图条目时在外部重新应用它。。。由于内部查询是按map_id分组的,因此不会有来自内部查询的重复项
这里是另一个选项,我想知道它的性能
SELECT
m.*,
FullTags.allTags
from
( SELECT
Just10.map_id,
GROUP_CONCAT(DISTINCT t.tag ORDER BY t.tag SEPARATOR ',') allTags
from
( SELECT mt.map_id
FROM map_tag mt
where mt.tag_id in ( select t.tag_id
from `tag` t
where t.tag in (...) )
group by mt.map_id
having COUNT(*) > 1
order by mt.map_id DESC
limit 0, 10 ) Just10
JOIN map_tag mt2
ON Just10.map_id = mt2.map_id
JOIN `tag` t
ON mt2.tag_id = t.tag_id
group by
Just10.map_id ) FullTags
JOIN map m
ON FullTags.map_id = m.map_id
最内部的查询最多只获取10个条目,这些条目包含多个与您正在查找的应用order by的标记匹配的条目。然后,仅针对这10条记录返回并获取组_concat()——同样,这只针对最多10条记录,然后最终加入以获取其余的地图数据。您能描述一下您希望查询执行的操作吗?当然。最终结果是从
map
表中获取整行,其中包含GROUP\u CONCAT
tags字段,该字段包含通过map\u标记和tag
表通过internal JOIN
s获得的属于map的标记。我能看懂这个问题。我不理解结果集的用途。结果集是从整个map
表中筛选出的结果,添加了属于该行的所有标记,并通过提供的标记进行筛选。目标是能够放入标记,然后取出带有这些标记的图。我希望属于这些映射的所有标记都在它们的行中,因此我不需要在原始查询的顶部查询每个标记。如果我不清楚,我很抱歉。谢谢你的回答,这个解决方案的问题是preTags
只包含中提供的标记和(…)中的t.tag
。它不考虑可能属于地图的任何其他标记。如果地图上有标签“aaa”、“bbb”和“ccc”,我在“aaa”、“bbb”中搜索和t.tag
,它将返回PreTags.allTags
作为“aaa、bbb”而不是“aaa、bbb、ccc”,如果这是我想要实现的目标,那么它将返回PreTags.allTags
。。。我已经修改了inner的查询以应用HAVING,但将group_concat过程中的所有标记作为默认值获取答案用于获取行中的连接标记,但它遇到了与我最初方法相同的性能问题。一旦我为map
表中的一列输入了orderby
,子查询就会转到临时文件排序并减慢速度。1.7秒用于查询解决方案,无需按map
列对结果进行排序,16.5秒用于排序。显然比我之前的47秒进步了一步,但我想知道为什么MySQL只有在orderby
或groupby
出现时才使用索引。@sidke,它在索引上失败,因为限定符来自您正在查找的另一个表中的特定标记值,而不是map_tags表中的索引的一部分,以利用索引的有效限定/排序方式。它必须先抓取每一条带有计数的记录,然后排序。@sidke,我用另一个可能的替代查询修改了答案,不知道这个查询将如何满足您的需要。
table index
map_tag ( map_id, tag_id )
tag ( tag_id, tag )
map ( map_id )
SELECT
m.*,
PreTags.allTags
from
( SELECT
mt.map_id,
GROUP_CONCAT(DISTINCT t.tag ORDER BY t.tag SEPARATOR ',') allTags
FROM
map_tag mt
JOIN `tag` t
ON mt.tag_id = t.tag_id
group by
mt.map_id
having
SUM( case when t.tag in (...) then 1 else 0 end ) > 1
order by
mt.map_id DESC ) PreTags
JOIN map m
ON PreTags.map_id = m.map_id
limit
0, 10
SELECT
m.*,
FullTags.allTags
from
( SELECT
Just10.map_id,
GROUP_CONCAT(DISTINCT t.tag ORDER BY t.tag SEPARATOR ',') allTags
from
( SELECT mt.map_id
FROM map_tag mt
where mt.tag_id in ( select t.tag_id
from `tag` t
where t.tag in (...) )
group by mt.map_id
having COUNT(*) > 1
order by mt.map_id DESC
limit 0, 10 ) Just10
JOIN map_tag mt2
ON Just10.map_id = mt2.map_id
JOIN `tag` t
ON mt2.tag_id = t.tag_id
group by
Just10.map_id ) FullTags
JOIN map m
ON FullTags.map_id = m.map_id