Justin的和Sebas的答案可以与LEFT扩展JOIN以消除‘间隙’,其是通常希望能适应的Oracle。
如果这是没有必要的,作为替代,我们可以去老同学Oracle日期算术......
SELECT TRUNC(t.time)+FLOOR(TO_CHAR(t.time,'sssss')/300)*300/86400 AS time
, AVG(t.value) AS avg_value
FROM foo t
WHERE t.time IS NOT NULL
GROUP BY TRUNC(t.time)+FLOOR(TO_CHAR(t.time,'sssss')/300)*300/86400
ORDER BY TRUNC(t.time)+FLOOR(TO_CHAR(t.time,'sssss')/300)*300/86400
让我们解开这一点。我们可以分开日期和时间组件,使用TRUNC获取日期部分,并使用TO_CHAR返回自午夜以来的秒数。我们知道5分钟是300秒,而我们知道一天有86400秒。因此,我们可以将秒数除以300,并将FLOOR(只是整数部分),它将我们向下舍入到最近的5分钟边界。我们将它乘以300(再乘以300),再次得到秒,然后将其除以一天中的秒数(86400),然后我们可以将它加回到(截断的)日期部分。
痛苦,是的。但非常快。
注:本传回四舍五入的时间值作为DATE
,这可能是如果需要转换回时间戳,但即使5分钟边界,一个DATE
具有足够的分辨率。
CREATE INDEX foo_FBX1
ON foo (TRUNC(t.time)+FLOOR(TO_CHAR(t.time,'sssss')/300)*300/86400,value);
附录:
由于这种方法的好处,对于一个大表,我们可以通过添加一个覆盖索引为此查询提升查询的性能MiMo为SQL Server提供了一个答案,表明它将适用于Oracle。这是Oracle对这种方法的改编。请注意,Oracle不提供DATEDIFF和DATEADD函数的等价物。 Oracle使用简单的算术代替。
SELECT TO_DATE('00010101','YYYYMMDD')+FLOOR((t.time-TO_DATE('00010101','YYYYMMDD'))*288)/288
AS time
, AVG(t.value) AS avg_value
FROM foo t
WHERE t.time IS NOT NULL
GROUP BY TO_DATE('00010101','YYYYMMDD')+FLOOR((t.time-TO_DATE('00010101','YYYYMMDD'))*288)/288
ORDER BY TO_DATE('00010101','YYYYMMDD')+FLOOR((t.time-TO_DATE('00010101','YYYYMMDD'))*288)/288
1月1日的选择,公元0001的基准日是任意的,但我不想与负值的混乱,并搞清楚,如果地板是正确的,或者我们是否需要使用负数的CEIL。 (神奇数字288是一天中1440分钟除以5的结果)。在这种情况下,我们将分数日,乘以1440并除以5,并取整数部分,然后再回到分数日。
很容易从PL/SQL包中获取“基准日期”,或从子查询中获取“基准日期”,但执行其中任何一项都可能会阻止此表达式成为确定性表达式。而且我们真的很想打开创建基于函数的索引的选项。
我的首选是避免在计算中包含“基准日期”。
Oracle版本10g +? – Sebas
是的,对不起 - 10g – Nick