选择表中每分钟的第一个值
Posted
技术标签:
【中文标题】选择表中每分钟的第一个值【英文标题】:Selecting first value of every minute in table 【发布时间】:2015-04-15 01:28:38 【问题描述】:我一直在尝试解决这个问题,也许我的问题是提出了正确的搜索查询。我不确定。
无论如何,我遇到的问题是我有一个数据表,该表每秒添加一个新行(想象一下结构 id, timestamp(datetime), value)。我想对 MySQL 进行一次查询以遍历表并仅输出每分钟的第一个值。
我曾考虑使用 LIMIT 和 datetime >=(分钟开始)的多个查询来执行此操作,但由于我收集的数据量很大,因此在单个查询中生成数据会更好查询。
样本数据:
id datetime value
1 2015-01-01 00:00:00 128
2 2015-01-01 00:00:01 127
3 2015-01-01 00:00:04 129
4 2015-01-01 00:00:05 127
...
67 2015-01-01 00:00:59 112
68 2015-01-01 00:01:12 108
69 2015-01-01 00:01:13 109
我希望结果选择行的位置:
1 2015-01-01 00:00:00 128
68 2015-01-01 00:01:12 108
有什么想法吗?
谢谢!
编辑:忘记添加,虽然每秒的数据并不可靠地在每分钟的第一秒 - 它可能是 :30 或 :01 而不是每分钟后的 :00 秒
编辑 2:一个不错的(绝对不需要回答)将是一个灵活的查询,也可以花费任意分钟数(而不是每分钟一行)
【问题讨论】:
添加您的代表性样本数据和预期结果。同时发布您解决问题的最佳方法。 您说,在给定的一分钟内,您的行数可以少于 60 行。如果某一分钟根本没有任何值,您希望在这一分钟的结果集中看到什么? 【参考方案1】:在 MS SQL Server 中我会使用 CROSS APPLY
,但据我所知 mysql 没有它,所以我们可以模拟它。
确保您的datetime
列上有索引。
创建一个table of numbers,或者在您的情况下创建一个分钟表。如果您有一个从 1 开始的数字表,那么将其转换为必要范围内的分钟是微不足道的。
SELECT
tbl.ID
,tbl.`dt`
,tbl.value
FROM
(
SELECT
MinuteValue
, (
SELECT tbl.id
FROM tbl
WHERE tbl.`dt` >= Minutes.MinuteValue
ORDER BY tbl.`dt`
LIMIT 1
) AS ID
FROM Minutes
) AS IDs
INNER JOIN tbl ON tbl.ID = IDs.ID
对于每一分钟,找到一个时间戳大于该分钟的行。我不知道如何返回整行,而不是在嵌套的SELECT
中返回 MySQL 中的一列,所以起初我正在制作一个包含两列的临时表:Minute
和 id
来自原始表然后显式查找原始表中的行,知道它们的IDs
。
SQL Fiddle
我在 SQL Fiddle 中创建了一个 Minutes 表,其中包含使示例简单的必要值。在现实生活中,您将拥有更通用的表格。
这里是SQL Fiddle,它使用了一个数字表,只是为了说明。
无论如何,您都需要提前知道您感兴趣的日期/数字的范围。
让它在任何分钟间隔内工作都是微不足道的。如果您需要每 5 分钟一次的结果,只需生成一个分钟表,该表的值不是每 1 分钟一次,而是每 5 分钟一次。主查询将保持不变。
可能效率更高,因为这里不将大表连接到自身,也不对datetime
列进行计算,所以服务器应该可以使用它上面的索引。
我所做的示例假设每分钟在大表中至少有一行。如果可能有一些分钟根本没有任何数据,您需要在 WHERE
子句中添加额外检查,以确保找到的行仍在该分钟内。
【讨论】:
你最终选择做什么?你是如何解决问题的?是否有任何答案足以让您接受答案?【参考方案2】:SELECT t2.* FROM
( SELECT MIN(`datetime`) AS dt
FROM tbl
GROUP BY DATE_FORMAT(`datetime`,'%Y-%m-%d %H:%i')
) t1
JOIN tbl t2 ON t1.dt = t2.`datetime`
SQLFiddle
或者
SELECT *
FROM tbl
WHERE dt IN ( SELECT MIN(dt) AS dt
FROM tbl
GROUP BY DATE_FORMAT(dt,'%Y-%m-%d %H:%i'))
SQLFiddle
SELECT t1.*
FROM tbl t1
LEFT JOIN (
SELECT MIN(dt) AS dt
FROM tbl
GROUP BY DATE_FORMAT(dt,'%Y-%m-%d %H:%i')
) t2 ON t1.dt = t2.dt
WHERE t2.dt IS NOT NULL
SQLFiddle
【讨论】:
DATE_FORMAT
为此目的似乎是一项相当昂贵的操作。
在我(诚然不是特别强大的)开发 MySQL 服务器上,这些查询都需要 30 秒才能完成 2,000,000 行,这确实是相当长的时间
@CallumA : 正如@Jordan 指出的那样,DATE_FORMAT
很贵,但我不知道其他解决方案。您可以尝试使用LEFT JOIN
,因为它认为是最快的反- 在 MySQL 中为非空列加入解决方案。
@CallumA :嗯,实际上,这令人印象深刻。但我不知道如何进一步改进其他可能的改进。)
@notulysses FLOOR(CAST(datetime as UNSIGNED) / 100)
再次更快,与DATE_FORMAT(...)
相比减少了 50% 以上!唯一的问题是我在日期时间 (DATETIME(3)) 中使用小数秒,而 MySQL 坚持在 CAST() 中四舍五入到最接近的值。这可以通过在施放之前使用SUBTIME(datetime, '0.5')
来纠正,但是在减少 50% 之后会增加 30% 的时间 -_- (尽管仍然比UNIX_TIMESTAMP
好),但结果更差(一开始不准确)。这够住一晚了,早上再看。【参考方案3】:
我不太确定,但你可以试试这个:
SELECT MIN(timestamp) FROM table WHERE YEAR(timestamp)=2015 GROUP BY DATE(timestamp), HOUR(timestamp), MINUTE(timestamp)
【讨论】:
【参考方案4】:select * from table where timestamp LIKE "%-%-% %:%:00"
可以工作。
这类似于这个问题:Stack Overflow Date SQL Query Question
编辑:这可能会更好:
`select , date_format(timestamp, '%Y-%m-%d %H:%i') as the_minute, count() 从表 按_分钟分组 按_分钟订购
此处与此问题类似:mysql select date format
【讨论】:
以上是关于选择表中每分钟的第一个值的主要内容,如果未能解决你的问题,请参考以下文章