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 |
+----+--------------------+--------------------+------+---------------------+------+---------+------+----------+----------+-------------+
答案 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
谓词。 (此外,查询返回表中的每一列,并且没有"覆盖"索引满足该SELECT列表。)
<强>后续强>
问:在这种情况下该怎么办?
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
dissidentList
和governmentList
的合适覆盖索引可以提高效果:
... 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操作。如果没有,并且dissidentList
和governmentList
中有很多匹配的行,则此查询可能会产生非常大的中间结果。如果此查询的EXPLAIN显示&#39;使用临时;使用filesort&#39;,这可能比原始效率低。如果查询的返回是一小部分事件,则可能更有效。