我在MySQL(5.5.31)中有一个表,它有大约20M行。以下查询:
SELECT DISTINCT mytable.name name FROM mytable
LEFT JOIN mytable_c ON mytable_c.id_c = mytable.id
WHERE mytable.deleted = 0 ORDER BY mytable.date_modified DESC LIMIT 0,21
导致全表扫描,解释说type
为ALL
,额外信息为Using where; Using temporary; Using filesort
。解释结果:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE mytable ALL NULL NULL NULL NULL 19001156 Using where; Using temporary; Using filesort
1 SIMPLE mytable_c eq_ref PRIMARY PRIMARY 108 mytable.id 1 Using index
没有连接解释如下:
id select_type table type possible_keys key key_len ref rows Extra
1 SIMPLE mytable index NULL mytablemod 9 NULL 21 Using where; Using temporary
id_c
是mytable_c
的主键,而mytable_c
mytable
中的每一行都没有多行。 date_modified
已编入索引。但看起来MySQL并不理解。如果我删除了DISTINCT子句,那么explain
使用索引并且只按预期触摸21行。如果我删除了连接,它也会这样做。有没有办法让它在没有连接的全表扫描的情况下工作? explain
显示mysql知道它只需要来自mytable_c
的一行并且它正在使用主键,但仍在mytable
上执行完全扫描。
DISTINCT的原因是ORM系统生成查询,其中可能存在JOIN生成多行的情况,但SELECT字段的值将始终是唯一的(即,如果JOIN是针对多个值链接只有每个连接行中相同的字段将在SELECT中。
答案 0 :(得分:1)
这些只是通用注释,而不是特定于mysql。
要查找name
中所有可能的mytable
值,需要对表或索引进行全面扫描。可能的选择:
deleted
开头的索引的完整索引扫描(利用过滤器)name
开头的索引的完整索引扫描(仅输出关注列)如果deleted
上有索引,则服务器可以找到所有deleted = 0
索引条目,然后从表中查找相应的name
值。但是如果deleted
基数较低或者统计数据不同,那么首先对索引进行双重读取然后对应的数据项可能会更加昂贵。在这种情况下,只需扫描表格。
如果name
上有索引,则索引扫描就足够了,但是需要检查表以查找过滤器。再次频繁地从索引跳到桌面。
还需要以类似的方式考虑连接列。
如果您忘记了连接部分并且在列name
,deleted
上有多部分索引,那么可能会发生索引扫描。
更新
对我来说,DISTINCT
和ORDER BY
部分有点令人困惑。其中name
条记录是用于排序的date_modified
?我觉得这样的事情会更清楚一点:
SELECT mytable.name name --, MIN(mytable.date_modified)
FROM mytable
LEFT JOIN mytable_c ON mytable_c.id_c = mytable.id
WHERE mytable.deleted = 0
GROUP BY mytable.name
ORDER BY MIN(mytable.date_modified) DESC LIMIT 0,21
无论哪种方式,一旦ORDER BY
发挥作用,就需要进行全面扫描才能找到订单。没有ORDER BY
,找到的前21个就足够了。
答案 1 :(得分:0)
为什么不尝试将条件 mytable.deleted = 0 从WHERE移到JOIN ON?您也可以尝试FORCE INDEX(mytablemod)