连接的WHERE子句为执行时间增加4秒

时间:2014-08-15 20:11:41

标签: mysql sql innodb

SELECT journey.id
FROM journey
JOIN journey_day ON journey_day.journey = journey.id
JOIN service ON journey.service = service.id
JOIN operator ON operator.id = service.operator
JOIN pattern ON pattern.id = journey.pattern
JOIN pattern_link pl ON pl.section = pattern.section AND pl.from_stop = "370023292"
JOIN pattern_link pl2 ON pl2.section = pl.section AND pl2.from_sequence < pl.from_sequence
WHERE CURDATE() BETWEEN service.date_start and service.date_end AND operator.id = "TMTL"

上面是一个SQL查询,平均需要0.1 - 0.3秒才能运行。

出于某种原因,只要我将AND journey_day.day = 3附加到WHERE子句,它就会为执行时间增加额外的4秒。因此我发布了这个问题。

journey_day表中的每一列都有某种索引。

id(INT 11) | journey(VARCHAR 128) | day(TINYINT 1)

id是主键,journeyday列都已编入索引。这个表完全没有问题,但是一旦WHERE子句对表格提出质疑,执行时间就会因为我的喜好而上升得太多了。

journey_day表就是确定journey运行的那一天。例如,如果旅程在星期三和星期四运行,那么旅程将在旅程列中具有其标识符,然后是3,在另一行中具有相同的标识但是具有4。

为什么会出现这么大的执行时间延迟?

修改

查询前面加EXPLAIN的结果:http://i.imgur.com/QrjZEHy.png

2 个答案:

答案 0 :(得分:0)

最可能的解释是,在添加该谓词时,MySQL正在选择不同的执行计划。

您可以比较两个查询的EXPLAIN输出,一个是journey_day.day=3谓词,另一个没有。

我猜测MySQL正在选择不同的连接顺序,并且当选择谓词时,MySQL选择使用day作为前导列的索引。并且很可能导致访问和检查更多行,或者可能MySQL在过滤掉行之前生成了一个大的中间集。

答案 1 :(得分:0)

由于您同时使用journey_day.journeyjourney_day.day列,因此多列索引比多个单列索引更有效。

来自"How MySQL Uses Indexes" on MySQL Reference Manual

假设您发出以下SELECT语句:

mysql> SELECT * FROM tbl_name WHERE col1=val1 AND col2=val2;

如果col1和col2上存在多列索引,则可以直接获取相应的行。如果col1和col2上存在单独的单列索引,优化程序将尝试使用索引合并优化(请参见第8.3.1.4节“索引合并优化”),或尝试通过确定哪个索引找到更少的索引来查找限制性最强的索引行并使用该索引来获取行。*

因此,您可以考虑创建索引,如下所示:

CREATE INDEX id_journey_day_id_day ON journey_day (journey, day);

在这种情况下,索引将由以下查询使用:

SELECT journey.id
FROM journey
JOIN journey_day ON journey_day.journey = journey.id AND journey_day.day = 3
JOIN service ON journey.service = service.id
JOIN operator ON operator.id = service.operator
JOIN pattern ON pattern.id = journey.pattern
JOIN pattern_link pl ON pl.section = pattern.section AND pl.from_stop = "370023292"
JOIN pattern_link pl2 ON pl2.section = pl.section AND pl2.from_sequence < pl.from_sequence
WHERE CURDATE() BETWEEN service.date_start and service.date_end AND operator.id = "TMTL";
相关问题