【问题标题】:How to find closest datetimes to 6-hour intervals?如何找到最接近 6 小时间隔的日期时间?
【发布时间】:2020-10-23 05:36:29
【问题描述】:

我有几百万条记录的表,其中包含指示全天随机时间 (datetime) 存在多少“事物”(int)。 (一天可能有 100 条或 1000 条记录。)

我需要报告大约 [00:00, 06:00, 12:00, 18:00] 每天存在多少“事物”...这意味着可能有也可能没有那些时间的记录,所以如果我不需要最近的记录(之前或之后)。

我尝试了一些方法,但运行时不适用于生产。必须有一种我没有想到的更有效的方法。这是我的尝试之一,但我也尝试使用UNION 查询,以及从 PHP 运行 4 个单独的查询。 (解决方案可以是 MySQL、PHP 或 JS/JQuery 中的任何一种)。

SELECT dt, SUM(things) thgs
FROM myTable
WHERE dt IN (SELECT MIN(dt) FROM myTable GROUP BY DATE(dt))
   OR dt IN (SELECT DATE_SUB(MIN(DATE_ADD(dt, INTERVAL 6 HOUR)), INTERVAL 6 HOUR) FROM myTable
             GROUP BY DATE_SUB(DATE(DATE_ADD(dt, INTERVAL 6 HOUR)), INTERVAL 6 HOUR))
   OR dt IN (SELECT DATE_SUB(MIN(DATE_ADD(dt, INTERVAL 12 HOUR)), INTERVAL 12 HOUR) FROM myTable
             GROUP BY DATE_SUB(DATE(DATE_ADD(dt, INTERVAL 12 HOUR)), INTERVAL 12 HOUR))
   OR dt IN (SELECT DATE_SUB(MIN(DATE_ADD(dt, INTERVAL 18 HOUR)), INTERVAL 18 HOUR) FROM myTable
             GROUP BY DATE_SUB(DATE(DATE_ADD(dt, INTERVAL 18 HOUR)), INTERVAL 18 HOUR))
GROUP BY dt
ORDER BY dt 

想法?

【问题讨论】:

    标签: javascript php mysql time group-by


    【解决方案1】:

    这个问题已经很晚了,但是当我向管理人员展示运营统计数据时,我通常是这样解决类似问题的,他们好奇有多少学生在任何给定时间使用我们的“学习门户”:

    SELECT tmp.`id`, tmp.`start_at`, tmp.`until_at`, COUNT(DISTINCT ss.`id`) as `things`
      FROM `Status` ss INNER JOIN (SELECT (h*1000+t*100+u*10+v+1) as `id`,
                                          DATE_SUB(DATE_FORMAT(DATE_ADD(Now(), INTERVAL 1 DAY), '%Y-%m-%d 00:00:00'), INTERVAL ((h*1000+t*100+u*10+v+1) - 1) * 6 HOUR) as `start_at`,
                                          DATE_SUB(DATE_SUB(DATE_FORMAT(DATE_ADD(Now(), INTERVAL 1 DAY), '%Y-%m-%d 06:00:00'), INTERVAL ((h*1000+t*100+u*10+v+1) - 1) * 6 HOUR), INTERVAL 1 SECOND) as `until_at`
                                     FROM (SELECT 0 h UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) a,
                                          (SELECT 0 t UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) b,
                                          (SELECT 0 u UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) c,
                                          (SELECT 0 v UNION SELECT 1 UNION SELECT 2 UNION SELECT 3 UNION SELECT 4 UNION SELECT 5 UNION SELECT 6 UNION SELECT 7 UNION SELECT 8 UNION SELECT 9) d
                                    WHERE (h*1000+t*100+u*10+v+1) > 0) tmp ON ss.`event_at` BETWEEN tmp.`start_at` AND tmp.`until_at`
     WHERE ss.`event_at` >= DATE_FORMAT(DATE_SUB(Now(), INTERVAL 2 WEEK), '%Y-%m-%d 00:00:00')
     GROUP BY tmp.`id`, tmp.`start_at`, tmp.`until_at`
     ORDER BY tmp.`id`;
    

    这是如何工作的:

    • 嵌套最多的子查询生成一个从 1 到 10,000 的数字序列
    • 此序列用于计算时间段(在本例中为 6 小时)
    • 7 年内大约有 10,000 个 6 小时周期
    • 将“事物”按start_atuntil_at 分组
    • 在合理的时间范围内过滤

    我在我的开发工作站上进行了测试,该工作站具有第四代酷睿 i7 和 32GB 内存。该查询只用了不到 30 秒的时间就返回了包含超过 2700 万条记录的统计表中的数据。

    这是一个示例……除了显示上述 SQL 之外,这并不意味着任何事情都是 cromulent:

    注意:开发框不包含当月的任何数据,因此存在差距。

    如果您不想显示“事物”为零的时段,则可以在之后将其过滤掉。

    如果您(或其他任何人)仍在寻找此类问题的解决方案,这可能会提供一些思考。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多