我在一个愚蠢的查询中失去了头发。首先,我会解释它的目标是什么。我每小时都会获取一组值并存储在数据库中。这些值可以随时间增加或保持相等。此查询最近60天提取最新值(我有双胞胎查询提取最新值的数周和月,它们是相似的)。该查询是不言自明的:
SELECT l.value AS value
FROM atable AS l
WHERE l.time = (
SELECT MAX(m.time)
FROM atable AS m
WHERE DATE(l.time) = DATE(m.time)
LIMIT 1
)
ORDER BY l.time DESC
LIMIT 60
看起来并不特别。但它非常慢(> 30秒),考虑到time
是一个索引,表包含的行少于5000行。而且我确定问题在于子查询。
noob错误在哪里?
更新1 :如果我使用MAX()
避免SELECT m.time ... ORDER BY m.time DESC
,则情况相同。
更新2 :多次调用DATE()
函数似乎不是问题。我试图创建一个计算字段day DATE
。 UPDATE atable SET day = DATE(time)
的运行时间不到2秒。带有l.day = m.day
(无函数!)的修改后的查询在与之前完全相同的时间内运行。
答案 0 :(得分:2)
我看到的主要问题是使用DATE()
子句中表达式左侧的WHERE
。在DATE()
表达式的两侧使用函数WHERE
显式阻止MySQL在日期字段上使用索引。相反,它必须扫描所有行以在每一行上应用该函数。
而不是:
WHERE DATE(l.time) = DATE(m.time)
尝试这样的事情:
WHERE l.time BETWEEN
DATE_SUB(m.date, INTERVAL TIME_TO_SEC(m.date) SECOND)
AND DATE_ADD(DATE_SUB(m.date, INTERVAL TIME_TO_SEC(m.date) SECOND), INTERVAL 86399 SECOND)
也许你知道一种更好的方法可以将m.date
转换为2012-02-09 00:00:00
和2012-02-09 23:59:59
之类的范围,而不是上面的例子,但想法是你要保留左侧的表达式作为原始列名,在这种情况下为l.time
,并在右侧以两个常量(或两个可转换为常量的表达式)的形式给出一个范围。
修改强>
我正在使用您预先计算的day
字段:
SELECT *
FROM atable a
WHERE a.time IN
(SELECT MAX(time)
FROM atable
GROUP BY day
ORDER BY day DESC
LIMIT 60)
至少在这里,内部查询只运行一次,然后使用IN
cluase完成二进制搜索。你仍然在扫描表格,但只有一次,内部查询只运行一次的优势可能会产生很大的影响。
如果您知道自己每天都有值,则可以通过添加WHERE
子句,将其限制为过去60个日历日,并丢失LIMIT 60
来改进内部查询。确保将day
和time
编入索引。
答案 1 :(得分:1)
不使用MAX(m.time)而是在子选择
中执行以下操作SELECT m.time
FROM table AS m
WHERE DATE(l.time) = DATE(m.time)
ORDER BY m.time DESC
LIMIT 1
这可能有助于加快查询速度,因为它为查询解析器提供了另一种选择
然而另一件我注意到的是你正在使用DATE(l.time)和DATE(m.time),如果你的索引没有在DATE(m.time)创建,那么你将不会使用索引和因此可能会导致缓慢。
答案 2 :(得分:1)
根据反馈答案,如果条目是按日期/时间顺序添加的,与自动增量ID直接相关,谁关心TIME ...获取auto-inc号码进行精确,非模糊的连接
select
A1.AutoID,
A1.time,
A1.Value
from
( select date( A2.time ) as SingleDate,
max( A2.AutoID ) as MaxAutoID
from aTable A2
where date( A2.Time ) >= date( date_sub( now(), interval 60 day ))
group by date( A2.time ) ) into MaxPerDate
JOIN aTable A1
on MaxPerDate.MaxAutoID = A1.AutoID
order by
A1.AutoID DESC
答案 3 :(得分:0)
您可以使用“explain”语句让mysql告诉您它正在做什么。
EXPLAIN SELECT l.value AS value
FROM table AS l
WHERE l.time = (
SELECT MAX(m.time)
FROM table AS m
WHERE DATE(l.time) = DATE(m.time) LIMIT 1
)
ORDER BY l.time DESC LIMIT 60
至少应该让你深入了解哪里可以进一步了解。
答案 4 :(得分:0)
如果您有time
的索引,我建议您使用TOP 1
代替MAX
,如下所示:
SELECT l.value AS value
FROM table AS l
WHERE l.time = (
SELECT TOP 1 m.time
FROM table AS m
ORDER BY m.time DESC LIMIT 1
)
ORDER BY l.time DESC LIMIT 60
答案 5 :(得分:0)
您的外部查询使用的是没有索引的文件集。 尝试更改为InnoDB引擎,看看它是否能改进。
快速测试:
mysql> show create table atable\G
*************************** 1. row ***************************
Table: atable
Create Table: CREATE TABLE `atable` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`t` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
KEY `t` (`t`)
) ENGINE=InnoDB AUTO_INCREMENT=51 DEFAULT CHARSET=utf8
1 row in set (0.00 sec)
mysql> explain SELECT id FROM atable AS l WHERE l.t = ( SELECT MAX(m.t) FROM atable AS m WHERE DATE(l.t) = DATE(m.t) LIMIT 1 ) ORDER BY l.t DESC LIMIT 50;
+----+--------------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+-------+---------------+------+---------+------+------+--------------------------+
| 1 | PRIMARY | l | index | NULL | t | 4 | NULL | 50 | Using where; Using index |
| 2 | DEPENDENT SUBQUERY | m | index | NULL | t | 4 | NULL | 50 | Using where; Using index |
+----+--------------------+-------+-------+---------------+------+---------+------+------+--------------------------+
2 rows in set (0.00 sec)
After changing to MyISAM:
mysql> explain SELECT id FROM atable AS l WHERE l.t = ( SELECT MAX(m.t) FROM atable AS m WHERE DATE(l.t) = DATE(m.t) LIMIT 1 ) ORDER BY l.t DESC LIMIT 50;
+----+--------------------+-------+-------+---------------+------+---------+------+------+-----------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+--------------------+-------+-------+---------------+------+---------+------+------+-----------------------------+
| 1 | PRIMARY | l | ALL | NULL | NULL | NULL | NULL | 50 | Using where; Using filesort |
| 2 | DEPENDENT SUBQUERY | m | index | NULL | t | 4 | NULL | 50 | Using where; Using index |
+----+--------------------+-------+-------+---------------+------+---------+------+------+-----------------------------+
2 rows in set (0.00 sec)