【发布时间】:2014-05-07 23:31:40
【问题描述】:
我在 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 中)。
【问题讨论】:
-
您在一个表上有一个外部联接,您没有从中选择任何列。我不明白。
-
@Strawberry 这个查询有点简化,但它仍然会导致全表扫描。这是奇怪的部分 - 无论我是否包含其他表中的字段,都会进行全表扫描。
-
试图在没有看到 a) 解释和 b) 正确 DDL 的情况下回答这类问题是愚蠢的。 (并不是说即使有了这些信息,我也能做得更好!)
-
@Strawberry 我已经添加了解释结果。我不认为 table defs 在这里很重要 - 任何类型的字段都会发生这种情况,但假设所有字段都是 int 或 varchars 并且 id、id_c 和 date_modified 都有索引。
-
这意味着这些列都不是PRIMARY?
标签: mysql sql select left-join distinct