MySQL不使用BETWEEN和IN条件的索引

时间:2014-09-19 04:03:42

标签: mysql

EXPLAIN SELECT *
FROM
  events AS e
  WHERE EXISTS (
        SELECT * FROM dissidentList
        WHERE actor_id IN (e.source_actor_id, e.target_actor_id)
              AND e.event_date BETWEEN start_date AND end_date)
    AND EXISTS (
        SELECT * FROM governmentList
        WHERE actor_id IN (e.source_actor_id, e.target_actor_id)
              AND e.event_date BETWEEN start_date AND end_date);

我在查询中出现的所有内容上都有索引(MUL),包括

events
-----------------------
event_date
source_actor_id
target_actor_id

dissidentList / governmentList
--------------------------
actor_id
start_date
end_date

然而,没有使用任何东西。为什么会这样?

+----+--------------------+--------------------+------+---------------------+------+---------+------+----------+----------+-------------+
| id | select_type        | table              | type | possible_keys       | key  | key_len | ref  | rows     | filtered | Extra       |
+----+--------------------+--------------------+------+---------------------+------+---------+------+----------+----------+-------------+
|  1 | PRIMARY            | e                  | ALL  | NULL                | NULL | NULL    | NULL | 19874715 |   100.00 | Using where |
|  3 | DEPENDENT SUBQUERY | anh_governmentList | ALL  | start_date,end_date | NULL | NULL    | NULL |   217890 |   100.00 | Using where |
|  2 | DEPENDENT SUBQUERY | anh_dissidentList  | ALL  | start_date,end_date | NULL | NULL    | NULL |    47452 |   100.00 | Using where |
+----+--------------------+--------------------+------+---------------------+------+---------+------+----------+----------+-------------+

1 个答案:

答案 0 :(得分:2)

问:为什么会这样?

A:可能OR条件是禁止MySQL考虑使用actor_id列上的索引。请注意,foo IN (a,b)(foo = a OR foo = b)

的简写

MySQL可能会使用复合索引,例如:

 ... ON dissidentList (start_date, end_date, actor_id)
 ... ON governmentList (start_date, end_date, actor_id)

EXPLAIN输出将显示"使用索引"如果MySQL使用覆盖索引。

对于表events,没有可搜索的谓词。 MySQL必须为EXISTS表中的每一行评估events谓词。 (此外,查询返回表中的每一列,并且没有"覆盖"索引满足该SEL​​ECT列表。)


<强>后续

问:在这种情况下该怎么办?

A:如果在dissidentList和governmentList上添加覆盖索引不会提高查询的性能,则可以使用JOIN操作生成等效结果。这可能会提供更好的性能,但这取决于执行计划和基数。

使用JOIN操作获得等效结果将要求events表具有PRIMARY KEY(或非空列上的UNIQUE KEY)。因为JOIN操作可以将多个匹配返回到events,所以我们需要添加GROUP BY以消除重复项,并且我们将使用PRIMARY KEY或UNIQUE KEY列。

假设id是PRIMARY KEY列的名称......

SELECT e.*
  FROM events e
  JOIN dissidentList f
    ON f.start_date <= e.event_date
   AND f.end_date   >= e.event_date
   AND f.actor_id   IN (e.source_actor_id, e.target_actor_id)
  JOIN governmentList g
    ON g.start_date <= e.event_date
   AND g.end_date   >= e.event_date
   AND g.actor_id   IN (e.source_actor_id, e.target_actor_id)
 GROUP BY e.id

dissidentListgovernmentList的合适覆盖索引可以提高效果:

... ON dissidentList (start_date, end_date, actor_id)
... ON governmentList (start_date, end_date, actor_id)

(我们希望EXPLAIN显示&#34;范围&#34;扫描操作,&#34;使用索引&#34;在额外列中。)

MySQL可以使用events上的PRIMARY KEY索引来优化GROUP BY操作。如果没有,并且dissidentListgovernmentList中有很多匹配的行,则此查询可能会产生非常大的中间结果。如果此查询的EXPLAIN显示&#39;使用临时;使用filesort&#39;,这可能比原始效率低。如果查询的返回是一小部分事件,则可能更有效。