【问题标题】:group MYSQL rows by datetime buckets starting at specific datetime按从特定日期时间开始的日期时间桶对 MYSQL 行进行分组
【发布时间】:2015-11-13 16:02:50
【问题描述】:

假设我有几列,其中一列的日期时间如下:

2015-07-19 17:00:01
2015-07-19 17:15:01
2015-07-19 17:30:01
2015-07-19 17:45:01
2015-07-19 18:00:01
2015-07-19 18:15:01
2015-07-19 18:30:01
2015-07-19 18:45:01
2015-07-19 19:00:01
2015-07-19 19:15:01
2015-07-19 19:30:01
2015-07-19 19:45:01
2015-07-19 20:00:01
2015-07-19 20:15:01

我希望输出每 3 小时循环一次以进行汇总:

2015-07-19 17:00:01, max(column B), etc
2015-07-19 20:00:01, max(column B), etc
2015-07-19 23:00:01, max(column B), etc
2015-07-20 02:00:01, max(column B), etc

我的尝试:

SELECT
datetime_col,
min(col_b)
FROM table
where datetime_col >= STR_TO_DATE('2015-07-19 17:00:01','%Y-%m-%d %H:%i:%s')
GROUP BY
YEAR(datetime_col),
MONTH(datetime_col),
DAY(datetime_col),
ROUND(HOUR(datetime_col)/3);

实际输出:

2015-07-19 17:00:01
2015-07-19 20:00:01
2015-07-19 23:00:01
2015-07-20 00:00:01
2015-07-20 02:00:01

您可以看到,在开始新的一天之前,分组看起来不错。无论日、月、年等,我都需要以 3 小时为周期对其进行分组。

最好我想在单个查询中执行此操作,因为我在 C# 应用程序中调用它;

【问题讨论】:

  • GROUP BY floor(unix_timestamp(datetime_col)/(3*3600)) 怎么样?

标签: mysql sql group-by


【解决方案1】:

简单的方法是创建一个包含日期和时间范围的表格。

这里是一个天的例子,你需要用小时扩展。

generate days from date range

所以你将拥有你的hour_block

id   startTime            endTime
 1   2015-07-19 00:00:01  2015-07-19 03:00:00
 2   2015-07-19 03:00:01  2015-07-19 06:00:00
 3   2015-07-19 06:00:01  2015-07-19 09:00:00
 ...         ...                ...
 8   2015-07-19 21:00:01  2015-07-20 00:00:00

然后你创建一个连接

 SELECT H.id, H.startTime, max(Y.column B)
 From YourTable Y
 JOIN hour_block H
   ON Y.datetime_col between H.startTime and H.endTime
 GROUP BY H.id, H.startTime

【讨论】:

  • 在范围条件下加入不能使用索引。
  • @Vatev 你能分享一些文档来描述这个事实吗?
  • 事实不需要文档 :) 在这种情况下,它实际上比范围条件问题更糟糕,因为连接使用 2 个不同的列,即使可以在一个范围内连接,它也只会有一个其中(在这种情况下与没有索引一样糟糕)。
  • @Vatev 是的,他们需要一个,如果你不提供证明只是一个假设。
  • 将其视为警告。任何决定使用它的人都应该检查查询计划并考虑性能影响。我的目标不是证明你错了或类似的事情。
【解决方案2】:

我会通过将值转换为最近的前 3 小时周期来处理这个问题。这很容易使用to_seconds(),但不允许转换回来。所以:

SELECT FROM_UNIXTIME(FLOOR(UNIX_TIMESTAMP(datetimecol) / (60 * 60 / 3) )) as datetime,
      min(col_b)
FROM table
WHERE datetime_col >= STR_TO_DATE('2015-07-19 17:00:01', '%Y-%m-%d %H:%i:%s')
GROUP BY FLOOR(UNIX_TIMESTAMP(datetimecol) / (60 * 60 / 3) )

【讨论】:

    【解决方案3】:

    假设你有 3 小时的间隔

    CREATE TABLE T
    (dcol datetime,b INT);
    
    INSERT INTO t
    VALUES('2015-07-19 17:00:01',23),
    ('2015-07-19 18:30:01',25),
    ('2015-07-19 19:00:01',66),
    ('2015-07-19 20:00:01',99),
    ('2015-07-19 21:00:01',5),
    ('2015-07-19 23:00:01',2),
    ('2015-07-20 02:00:01',78),
    ('2015-07-20 03:00:01',9),
    ('2015-07-20 05:00:01',11),
    ('2015-07-20 07:00:01',29)
    
    SELECT t.dcol,t.b
    FROM t JOIN t tt WHERE t.dcol=tt.dcol+INTERVAL 3 HOUR
    UNION
    SELECT tt.dcol,tt.b
    FROM t JOIN t tt WHERE t.dcol=tt.dcol+INTERVAL 3 HOUR
    ORDER BY dcol
    
    dcol                 b
    2015-07-19 17:00:01  23
    2015-07-19 20:00:01  99
    2015-07-19 23:00:01  2
    2015-07-20 02:00:01  78
    2015-07-20 05:00:01  11
    

    【讨论】:

    • 这行不通。我需要分组,以便我可以在 3 小时内对值进行一些聚合
    猜你喜欢
    • 1970-01-01
    • 2016-10-28
    • 1970-01-01
    • 2013-01-01
    • 1970-01-01
    • 2013-01-16
    • 2011-11-28
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多