php - 按固定日期间隔选择并分组

标签 php mysql sql datetime intervals

我正在研究一个基于 MySQL 时间的大型统计表。

我有一个固定的时间范围(开始和结束日期时间对象)并在 ISO-8601 中获取间隔字符串像 P1DT6H 一样的间隔字符串作为 PHP 对象,其中范围中的开始日期也是间隔的起点,并且还定义了间隔使用的时区。

现在我想选择给定时间范围内按此间隔分组的所有数据,但即使经过很多小时也无法使其工作:(

例如,我得到的时间范围为 2015-09-01/2015-09-06 ,间隔为 P1DT6H 以及以下示例表:

TIMESTAMP           | count
2015-09-01 00:00:00 | 1
2015-09-01 02:00:00 | 1
2015-09-01 04:00:00 | 1
2015-09-01 06:00:00 | 1
2015-09-01 08:00:00 | 1
2015-09-01 10:00:00 | 1
2015-09-01 12:00:00 | 1
2015-09-01 14:00:00 | 1
2015-09-01 16:00:00 | 1
2015-09-01 18:00:00 | 1
2015-09-01 20:00:00 | 1
2015-09-01 22:00:00 | 1
2015-09-03 00:00:00 | 1
2015-09-03 02:00:00 | 1
2015-09-03 04:00:00 | 1
2015-09-03 06:00:00 | 1
2015-09-03 08:00:00 | 1
2015-09-03 10:00:00 | 1
2015-09-03 12:00:00 | 1
2015-09-03 14:00:00 | 1
2015-09-03 16:00:00 | 1
2015-09-03 18:00:00 | 1
2015-09-03 20:00:00 | 1
2015-09-03 22:00:00 | 1
2015-09-05 00:00:00 | 1
2015-09-05 02:00:00 | 1
2015-09-05 04:00:00 | 1
2015-09-05 06:00:00 | 1
2015-09-05 08:00:00 | 1
2015-09-05 10:00:00 | 1
2015-09-05 12:00:00 | 1
2015-09-05 14:00:00 | 1
2015-09-05 16:00:00 | 1
2015-09-05 18:00:00 | 1
2015-09-05 20:00:00 | 1
2015-09-05 22:00:00 | 1

我希望得到以下结果:

TIMESTAMP           | count
2015-09-01 00:00:00 | 12
2015-09-02 06:00:00 | 6
2015-09-03 12:00:00 | 6
2015-09-04 18:00:00 | 12

当然间隔可以更复杂,时间范围可以很大,数据表也很大。

这需要处理几乎每个月都有不同天数的月份,包括。闰年和夏令时也会发生变化,一天可以有 23、24 或 25 小时。 (意味着一天间隔不同于 24 小时间隔)

如果有人有解决方案或者我能为此类问题指出正确的方向,那将非常有帮助。

谢谢!

PS:我有一个脚本,可以在给定数据库列、开始、结束和间隔对象的基础上创建 SQL 表达式,但它仅适用于非常简单的间隔,例如 P1D。我不会在这里跳过它,因为我不想让所有伟大的大脑进入我已经拥有的非工作方向;)

我现在拥有的,但它不适用于混合间隔。 示例:

时区处理:

if ($db->getTimezone()->getName() !== $start->getTimezone()->getName()) {
    $col = 'CONVERT_TZ(' . $col
        . ', ' . $this->quote($this->getTimezone()->getName())
        . ', ' . $this->quote($start->getTimezone()->getName())
        . ')';
}

P1M:

$m = ($interval->y * 12) + $interval->m;
if ($m) {
    if ($m > 1) {
        $mod  = $start->format('Ym') % $m;
        $mod  = $mod ? ' + ' . $mod : '';
        $expr = 'EXTRACT(YEAR_MONTH FROM ' . $col . ')';
        $expr = $mod ? '(' . $expr . $mod . ')' : $expr;
        $expr = ' - INTERVAL ' . $expr . ' % ' . $m . ' MONTH';
        $sqlIntervalMonth = $expr;
    }
    $sqlIntervalDay = ' - INTERVAL DAY(' . $col . ') - 1 DAY';
    if ($start->format('d') > 1) {
        $sqlIntervalDay .= ' + INTERVAL ' . ($start->format('d') - 1) . ' DAY';
    }
}

P1D:

$d = $interval->d;
if ($d) {
    $days = $start->diff(new DateTime('0000-00-00'))->days;
    $mod  = $days % $d;
    $mod  = $mod ? ' + ' . $mod : '';
    $expr = 'TO_DAYS(' . $col . ')';
    $expr = $mod ? '(' . $expr . $mod . ')' : $expr;
    $expr = ' - INTERVAL ' . $expr . ' % ' . $d  . ' DAY';
    $sqlIntervalDay = $expr;
}

编辑1:指出时区、夏令时和闰年要求。 编辑2:添加了PHP片段

最佳答案

一个想法是将时间戳的值转换为秒,然后将该值舍入到适当的间隔(在您的情况下为 36 小时)。像这样的事情:

select min(timestamp) as timestamp, sum(count)
from t
group by floor(to_seconds(timestamp) / 60 * 60 * 36)  -- * 60 * 60 * 36
order by timestamp;

这使用了日期时间值的min(),因为表中似乎有该值。或者,您可以将四舍五入的秒数转换回日期时间值。

关于php - 按固定日期间隔选择并分组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34333558/

相关文章:

php - 如果 true 使用 foreach 循环,否则使用 while

php - 同步 MySQL 和 PHP 时间

javascript - jQuery 仅选择主表中的 tr/td,而不选择嵌套表

带有自定义显示的 PHP/mysql 搜索

php - 我的 PHP 文件没有正确解析

mysql - str_to_date() 不适用于正确的语法

sql - Oracle SQL添加多行表注释或列注释

sql - 虚拟 where 子句对性能的影响

sql - ORA-01779 : cannot modify a column which maps to a non key-preserved table

php - 使用Twilio的短信验证系统