选择表中每分钟的第一个值

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 中的一列,所以起初我正在制作一个包含两列的临时表:Minuteid 来自原始表然后显式查找原始表中的行,知道它们的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

【讨论】:

以上是关于选择表中每分钟的第一个值的主要内容,如果未能解决你的问题,请参考以下文章

表中每一行的不同菜单项

有没有办法在 SQL 中每分钟只选择第一项?

使用jQuery在IE8中选择HTML表格每一行的第一个TD

如何在 C# winform 中每分钟自动调用一个方法

需要在android中每分钟运行一个后台任务

在 Swift 中每 x 分钟做一次