mysql日期查询始终执行完全扫描

mysql日期查询始终执行完全扫描,mysql,sql,performance,indexing,Mysql,Sql,Performance,Indexing,我试图做一个假设非常简单的查询。我有一个带有datetime列和时间戳的表 我需要找到并没有最后5分钟时间戳的所有父表行。这可能会按如下所述逐行更改。我读了很多文章,试图改变我的查询,但我的查询仍然没有正确使用索引 1) 下面显示的访问表可能有多行mon.id。 2) 我需要查找access表中没有行的所有mon.id,其lastaccess\u date datetime在最后一个mon.duration分钟内。 3) access表可能有多行,因此需要检查带有最新时间戳的行的持续时间逻辑 各

我试图做一个假设非常简单的查询。我有一个带有datetime列和时间戳的表

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

1) 下面显示的访问表可能有多行mon.id。 2) 我需要查找access表中没有行的所有mon.id,其lastaccess\u date datetime在最后一个mon.duration分钟内。 3) access表可能有多行,因此需要检查带有最新时间戳的行的持续时间逻辑

各表如下:

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解释如下:

编辑:根据评论和我之前尝试过的方法,我甚至将查询更改为剧烈的:

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行

下面是现在的解释:

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'

第二个查询的
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() )
这符合索引范围扫描的条件

第三,您在第一次查询中似乎误用了
groupby
。你是说按订购吗?很难弄清楚你需要什么。请阅读以下内容:

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

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)

在DATETIME列上使用函数会使索引的使用失败。重铸查询以消除该函数将允许利用索引。感谢您对我的查询@OllieJones的深刻见解!我会生成一些测试数据,尝试你的建议,然后在这里报告。我尝试使用GROUP BY的原因是,子表具有多个父id的时间戳行。我尝试检查最新的datetime唯一父id,然后对其执行日期检查。但现在看来我可能需要按照你的建议去做
  ...
  FROM mon
 INNER JOIN access_log LOG 
       ON mon.id = log.monitor_id
     AND log.lastaccess_date < DATE_SUB(NOW(),INTERVAL mon.duration MINUTE)
 (monitor_id, lastaccess_date)
 (lastaccess_date, monitor_id)