mysql日期查询总是进行全扫描

时间:2015-01-22 03:18:58

标签: mysql sql performance indexing

我正在尝试做一个非常简单的查询。我有一个带有时间戳的datetime列的表。

我需要找到没有最后5分钟时间戳的所有父表行。这可以如下所述地逐行改变。我阅读了很多文章,尝试更改我的查询,但我的查询仍未正确使用索引。

1)下面显示的访问表可能有多行mon.id. 2)我需要找到所有在访问表中没有行的mon.id,并且lastaccess_date日期时间在最后的mon.duration分钟内。 3)访问表可能有多于1行,因此需要检查具有最新时间戳的行的持续时间逻辑。

表格如下:

mon (parent)
-----------
id,payload,duration

access (child)
---
id,mon_id,lastaccess_date

当前查询

select id,payload,elapsed,duration from 
(SELECT mon.id,payload,TIMESTAMPDIFF(MINUTE, lastaccess_date, NOW()) as elapsed,duration
    FROM mon
    inner JOIN access_log log on mon.id=log.monitor_id
order by lastaccess_date desc
 ) as t1
GROUP BY id
having elapsed>duration

我还提出了许多其他查询,但这些查询效率似乎不高。如果我有100行,那么这些查询不使用索引并进行全表扫描。

请建议一个可以使用索引的高效查询。如果需要,我可以稍微调整一下表设计,如果它有助于这种情况。

这个查询的mysql EXPLAIN如下所示:

enter image description here

编辑:根据评论,以及我之前尝试过的内容,我甚至将查询更改为激烈的:

select monitor_id
  from access_log
 WHERE access_dt not between date_sub(now(),INTERVAL 5 MINUTE) and now()

现在我没有触及where子句中的access_dt DATETIME列,但仍在进行全表扫描。在此测试场景中,查询返回100行中的40行。

现在是EXPLAIN:

id, select_type, table, type, possible_keys, key, key_len, ref, rows, filtered, Extra
'1', 'SIMPLE', 'access_log', 'ALL', 'access_dt', NULL, NULL, NULL, '100', '100.00', 'Using where'

1 个答案:

答案 0 :(得分:0)

您的第二个查询EXPLAIN有多种可能性不符合您的期望。

首先,不要浪费时间担心小桌子的EXPLAIN结果。这是一个很小的表,你的查询返回了一半以上。 MySQL查询规划器可能没有选择索引只是因为它似乎没有足够的选择性值得分配到RAM和使用的麻烦。如果是这种情况,情况可能随着你的桌子的增长而改变。

其次,你有这个条款:

WHERE access_dt not between date_sub(now(),INTERVAL 5 MINUTE) 
                        and now()

not可能被证明是无益的,因为它的表现就好像是

WHERE (    access_dt < date_sub(now(),INTERVAL 5 MINUTE)
        OR access_dt > now() )
对于MySQL来说,

OR条款并不好玩。如果您碰巧知道将来不能使用access_dt值,那么您可以这样做。

WHERE access_dt < date_sub(now(), INTERVAL 5 MINUTE)

并且有资格进行索引范围扫描。

第三,您似乎在第一次查询中滥用了GROUP BY。你的意思是ORDER BY?很难弄清楚你需要什么。阅读:http://dev.mysql.com/doc/refman/5.6/en/group-by-handling.html

最后,让我们在您的第一个查询中查看您的内部查询,并尝试对其进行优化。您从这开始,我已经编辑过以显示每列所来自的表。

SELECT mon.id, mon.payload,
      TIMESTAMPDIFF(MINUTE, log.lastaccess_date, NOW()) as elapsed,
      mon.duration
 FROM mon
inner JOIN access_log log ON mon.id=log.monitor_id
order by log lastaccess_date desc

让我们通过在您的ON子句中添加时间戳选择标准来调整此项。

  ...
  FROM mon
 INNER JOIN access_log LOG 
       ON mon.id = log.monitor_id
     AND log.lastaccess_date < DATE_SUB(NOW(),INTERVAL mon.duration MINUTE)

这将选择您想要的行。当你获得相对较大的表(access_log中至少10K行)时,你应该试验下面两个复合索引,看看是否有一个给你更好的结果。

 (monitor_id, lastaccess_date)
 (lastaccess_date, monitor_id)