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