我正在研究一个基于 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/