当连接更改时,MySQL SELECT将永远运行

时间:2009-08-26 16:05:02

标签: sql mysql performance query-optimization

我有以下SQL代码:

select val.PersonNo,
       val.event_time,
       clg.number_dialed
  from vicidial_agent_log val
         join
       call_log           clg on date_add('1970-01-01 02:00:00', interval clg.uniqueid second) = val.event_time
 order by val.event_time desc
 limit 100;

在不到1秒的时间内执行并返回行。但是,如果我将直接连接更改为left outer

select val.PersonNo,
       val.event_time,
       clg.number_dialed
  from vicidial_agent_log val
         left outer join
       call_log           clg on date_add('1970-01-01 02:00:00', interval clg.uniqueid second) = val.event_time
 order by val.event_time desc
 limit 100;

查询永远运行并使用服务器CPU的~100%。

我在两个查询上运行explain,第一个命中event_time上的vicidial_agent_log索引,而第二个忽略所有索引。 call_log.uniqueid上有一个索引。

vicidial_agent_log包含~41,000行,call_log包含~43,000。

所以我的问题是 - 为什么MySQL没有达到我定义的索引,有没有办法强制它这样做,如果没有,我怎么能让这个查询以可接受的速度运行?

修改

完整解决方案:

select val.PersonNo,
       val.event_time,
       cl.number_dialed
  from vicidial_agent_log val
         left outer join
       (select date_add('1970-01-01 02:00:00', interval clg.uniqueid second) as 'converted_date',
               number_dialed
          from call_log clg) cl ON cl.converted_date = val.event_time
 order by val.event_time desc
 limit 100;

5 个答案:

答案 0 :(得分:3)

当您使用LEFT JOIN时,LEFT表始终位于MySQL

在您的初始查询中,MySQL可以选择要进行领导的表格,并选择clg

现在它无法选择,而且这个条件:date_add('1970-01-01 02:00:00', interval clg.uniqueid second)不是可以选择的。

date_add('1970-01-01 02:00:00', interval clg.uniqueid second)上没有MySQL可用于查找val.event_time值的索引。

重写您的查询:

SELECT  val.PersonNo,
        val.event_time,
        clg.number_dialed
FROM    vicidial_agent_log val
LEFT OUTER JOIN
        call_log clg
ON      clg.uniqueid = UNIX_TIMESTAMP(val.event_time) - 7200
ORDER BY
        val.event_time desc
LIMIT 100

答案 1 :(得分:1)

我认为外连接正在强制进行表扫描,因为它需要包含所有匹配的记录,并为那些不匹配的记录提供空值。

mck89的解决方案可能会很好用,但我从来没有理由使用它......我很好奇这将如何发展。

答案 2 :(得分:1)

第一个可以使用索引,因为在内连接中,您将根据索引所基于的列(Event_Time)的值来过滤连接的结果集...

在第二个查询中,您正在使用外部联接,您不会过滤输出,因此无论event_time的值如何,它都需要包括结果集中的所有记录,因此它必须执行完整的表扫描......

答案 3 :(得分:1)

使用JOIN或WHERE子句中的函数将始终对索引造成严重破坏。例如:

DATE_ADD('1970-01-01 02:00:00', INTERVAL clg.uniqueid SECOND)

数据库正在使用uniqueid索引来查找转换的值,而不是与您的case中的event_time列进行比较。如果这是启用了PLW错误的Oracle,则会通知您有可能从数据类型转换出来。

应始终在比较之前处理此类情况,这意味着使用内联视图执行转换,然后加入到结果列。 IE:

JOIN (SELECT DATE_ADD('1970-01-01 02:00:00', INTERVAL clg.uniqueid SECOND) 'converted_date'
        FROM CALL_LOG clg) cl ON cl.converted_date = val.event_time

答案 4 :(得分:-1)

您可以使用FORCE INDEX