MySQL DATE_ADD在动态间隔下运行得太慢

时间:2014-09-30 00:35:29

标签: mysql query-optimization dateadd

我有以下查询在数千条记录上执行时运行速度很慢。

SELECT
    name,
    id
FROM
   meetings
WHERE
  meeting_date < '2014-09-20 11:00:00' AND (
  meeting_date >= '2014-09-20 09:00:00' OR
  DATE_ADD(meeting_date, INTERVAL meeting_length SECOND) > '2014-09-20 09:00:00'
)

查询会检查meeting_date2014-09-20 09:00:00之间的2014-09-20 11:00:00是否重叠。以上查询涵盖了所有可能的重叠案例。但是,DATE_ADD会增加很多开销。

无论如何要优化DATE_ADD?删除DATE_ADD极大地提高了性能,但它不会涵盖所有重叠的情况。

1 个答案:

答案 0 :(得分:2)

我建议您删除OR。当该列被包装在一个函数中时(当没有在裸列上执行比较,但是比较时),MySQL不会对meeting_date上的索引执行范围扫描操作(不能)#(t = t; t)到必须为每一行计算的表达式的结果。)

对于大型表,显然是前导列为meeting_date的索引。

我认为&#34;技巧&#34;获得更好的性能是重写查询以引入一些额外的领域知识。具体来说,meeting_length的MINIMUM和MAXIMUM值是多少?

我认为假设它不会是消极的,这是非常安全的。我们可能不希望它为零。但即使最小长度大于零,我们也可以使用零作为我们已知的&#34;已知的#34;最小。 (事实证明它比其他非零值更方便。)

我们真正需要知道的是meeting_length的MAXIMUM值。如果这是一个已知的常量值,那就太好了,因为我们将在查询中包含该值。我们假设meeting_length的最大值是7天内的秒数。

作为我所想的事情的证明:

SELECT m.name
     , m.id
  FROM meetings m
 WHERE m.meeting_date  < '2014-09-20 11:00:00' 
   AND m.meeting_date  > '2014-09-20 09:00:00' + INTERVAL -7 DAY
HAVING m.meeting_date  + INTERVAL meeting_length SECOND 
                       > '2014-09-20 09:00:00'

让我们解开一点。

第一个谓词与原始查询中的相同...&#34; start&#34;会议时间之前&#34;结束&#34;指定期间。

第三个谓词也与查询中的相同...&#34; end&#34; 指定期间的开头之后会议。 (我个人的偏好是使用+ INTERVAL表单为日期时间添加持续时间。)

因此,就像原始查询一样,我们正在寻找重叠。

我建议我们包含另一个可搜索的谓词。鉴于我们对meeting_length已知最小值为0,因此添加此谓词并不会真正改变对重叠的检查。它做的是添加一个我们可以检查的固定下限。

要解释一下......如果满足条件&#34;会议结束的会议行是在句号开始之后&#34;,那么我们也知道,对于那一行,#34;会议开始是在(期间开始MINUS会议长度)&#34;。而且我们也知道&#34;会议开始时间之后(期间开始减去会议长度的最大可能值。

对于大多数行来说,这将是一个更大的范围......但是&#34;技巧&#34;是检查可以比较&#34;裸&#34;的谓词。反对常数的列。

这意味着MySQL将能够使用索引范围扫描操作来满足这一要求。查询的格式为:

 WHERE meeting_date > const 
   AND meeting_date < const

这对索引范围扫描来说非常完美。这应该有利于性能...假设有一个合适的索引,并且显着限制了需要检查的行数。

但就其本身而言,返回的行数超出了我们的需要,我们将会在会议开始之前开始和结束会议。

因此我们仍然需要额外检查,以进一步过滤行。但是不必为每一行评估,只有通过前两个谓词的行。

   AND meeting_date + length > const

我们只需要让MySQL认识到length永远不会消极;认识到这实际上是一个更严格的&#34;范围,而不是更广泛的范围。它可能适用于AND,但我们可以强制MySQL稍后通过将其包含在HAVING子句中来评估该条件。

HAVING meeting_date + length > const

但是,所有这些只是猜测。

我们真的需要看一下EXPLAIN输出。

如果带有meeting_date前导列的索引也包含id和name列,则MySQL可以完全从索引中满足查询,而无需引用基础表中的页面。 (如果发生这种情况,我们会在EXPLAIN输出中看到&#34;使用索引&#34;)


早些时候,我说如果我们有一个已知的常数最大meeting_length会很方便。

我们还可以使用查询从数据中确定:

SELECT MAX(meeting_length) FROM meetings

(使用meeting_length作为前导列的索引将避免对表进行昂贵的全扫描)

我们使用该值来推导&#34;常数&#34;谓词中的值。

我们可以包含该查询(作为内联视图或子查询),但这可能会影响性能。 (我们需要测试&#34;智能&#34; MySQL优化器是......

我们可以尝试将其作为子查询:

SELECT m.name
     , m.id
  FROM meetings m
 WHERE m.meeting_date  < '2014-09-20 11:00:00' 
   AND m.meeting_date  > '2014-09-20 09:00:00' 
                       - INTERVAL (SELECT MAX(l.meeting_length) FROM meetings l) DAY
HAVING m.meeting_date  + INTERVAL meeting_length SECOND 
                       > '2014-09-20 09:00:00'

或者尝试将其作为内联视图:

SELECT m.name
     , m.id
  FROM ( SELECT MAX(l.meeting_length) AS max_seconds
           FROM meetings l
       ) d
 CROSS
  JOIN meetings m
 WHERE m.meeting_date  < '2014-09-20 11:00:00' 
   AND m.meeting_date  > '2014-09-20 09:00:00' 
                       - INTERVAL d.max_seconds SECOND
HAVING m.meeting_date  + INTERVAL meeting_length SECOND 
                       > '2014-09-20 09:00:00'