【问题标题】:Select and Group by fixed date interval按固定日期间隔选择和分组
【发布时间】:2016-03-23 20:39:14
【问题描述】:

我正在处理一个基于 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

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

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

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

谢谢!

PS:我有一个脚本,可以在给定的 db 列、开始、结束和间隔对象的基础上创建 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 sn-ps

【问题讨论】:

  • 您能否阐明您如何从数据中获得预期结果?自己看不到....
  • 这是一个 MySQL TIMESTAMP 数据类型字段,示例结果正是我所需要的。几乎所有向客户显示数字的统计应用程序都需要处理时区。您不能简单地忽略没有人居住在 UTC 的现实。
  • 我的意思是我有一个起点2015-09-01 和一个间隔P1DT6H,我需要将所有数据组合在一起,一个间隔点到下一个间隔点等。
  • Ryan - 我为我现在拥有的时区处理添加了片段 - 请参阅 Timezone handling:

标签: php mysql sql datetime intervals


【解决方案1】:

一个想法是将时间戳的值转换为秒,然后将该值四舍五入到适当的间隔(在您的情况下为 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(),因为您似乎在表中有该值。或者,您可以将四舍五入的秒数转换回日期时间值。

【讨论】:

  • 此查询不会返回不存在的数据点,例如 2015-09-02 06:00:00,并且它不适用于 dst 时区或管理月迭代,因为并非每个月都有相同的天数。
【解决方案2】:

这是我能得到的最接近的。虽然没有任何数据的时间段不会被表示(不知道这是否有问题):

select yourTimestamp,sum(yourCount)
from
(
select t.yourTimestamp,t.yourCount,
'2015-09-01' + interval (36*60)*(floor(timestampdiff(MINUTE,'2015-09-01',t.yourTimestamp)/(36*60))) minute as recordGrpTime
from t
where t.yourTimestamp between '2015-09-01' and '2015-09-06'
) t
group by recordGrpTime;

【讨论】:

  • 嗨,汤姆,问题是 1 天!= 24 小时,因为 dst 差异
猜你喜欢
  • 1970-01-01
  • 2015-03-01
  • 1970-01-01
  • 2021-02-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-02-25
  • 1970-01-01
相关资源
最近更新 更多