使用Oracle,从微秒到6秒的间隔

时间:2012-02-18 13:57:46

标签: sql oracle intervals

我有高频率滴答数据,需要从微秒到6秒间隔排序。每日蜱数据从美国东部时间09:15:00开始,到美国东部时间15:15:00结束。

这是我的temp2表:

 date1                       | close1 | volume1
-----------------------------+--------+---------
 2010-04-16 09:15:28.010 EST |  10001 |       4
 2010-04-16 09:16:00.020 EST |  10002 |       5
 2010-04-16 09:16:35.030 EST |  10003 |       6
 2010-04-16 09:16:35.040 EST |  10001 |       3
 2010-04-16 15:14:59.050 EST |  10007 |       3
 2010-04-19 09:15:05.050 EST |  10002 |       1
 ...                         |    ... |     ...

date1VARCHAR2(28); close1volume1都是NUMBER s。

如何获得以下结果?

 date2               | close2 | volume2
---------------------+--------+---------
 2010-04-16 09:15:30 |  10001 |       4
 2010-04-16 09:16:06 |  10002 |       5
 2010-04-16 09:16:36 |  10001 |       9
 2010-04-16 15:15:00 |  10007 |       3
 2010-04-19 09:15:06 |  10002 |       1
 ...                 |    ... |     ...

close2列使用间隔00,06,12,18,24,30,36,42,48,54的last_value。如果在00-05.999范围内的微秒,则只需设置close2列值为间隔的last_vlue,将volume2列值设置为间隔的volume1之和。

2 个答案:

答案 0 :(得分:2)

问题的核心是将您拥有的时间戳转换为与您的6秒间隔相对应的“桶”。一种方法是将日期部分提取到分钟,这可以使用TRUNC函数完成,并将秒数作为单独的值。 (请注意,我正在将date1视为已经是TIMESTAMP,正如评论中所建议的那样,以及对您之前问题的回答,但如果它仍然是VARCHAR那么你需要转换它):

select trunc(date1, 'MI'), extract(second from date1) from temp2;

2010-04-16 09:15:00   28.01
2010-04-16 09:16:00     .02
2010-04-16 09:16:00   35.03
2010-04-16 09:16:00   35.04
2010-04-16 15:14:00   59.05
2010-04-19 09:15:00    5.05

...然后操纵秒部分让它围绕你的窗口:

select trunc(date1, 'MI'),
    6 * (trunc(1 + extract(second from date1) / 6))
from temp2;

2010-04-16 09:15:00   30.00
2010-04-16 09:16:00    6.00
2010-04-16 09:16:00   36.00
2010-04-16 09:16:00   36.00
2010-04-16 15:14:00   60.00
2010-04-19 09:15:00    6.00

...最后将它们加在一起,通过将'秒'窗口跳过86400(一天中的秒数)来进行日期算术:

select trunc(date1,'MI')
    + (6 * trunc(1 + (extract(second from date1) / 6)))/(24*60*60)
from temp2;

...当你加倍然后除以时,这可以略微减少到:

select trunc(date1,'MI')
    + trunc(1 + (extract(second from date1) / 6))/(24*60*10)
from temp2;

2010-04-16 09:15:30
2010-04-16 09:16:06
2010-04-16 09:16:36
2010-04-16 09:16:36
2010-04-16 15:15:00
2010-04-19 09:15:06

现在,您可以使用此作为某些analytic functions的基础来获取其他字段所需的值,例如:

select distinct date2,
    first_value(close1) over (partition by date2 order by date1 desc) as close2,
    sum(volume1) over (partition by date2) as volume2
from (
    select date1, close1, volume1, trunc(date1,'MI')
        + trunc(1 + (extract(second from date1) / 6))/(24*60*10) as date2
    from temp2
)
order by date2, close2, volume2;

DATE2                   CLOSE2    VOLUME2
------------------- ---------- ----------
2010-04-16 09:15:30      10001          4
2010-04-16 09:16:06      10002          5
2010-04-16 09:16:36      10001          9
2010-04-16 15:15:00      10007          3
2010-04-19 09:15:06      10002          1

我正在将日期的间隔版本添加到现有列上,并使整个内容成为内联视图,因为我们也需要所有原始列;并且对于每个date2我使用FIRST_VALUE函数(在排序中使用close1)以及SUM的分析版本获取最新的desc值获得该窗口的volume1总数。 distinct就在那里,因为anayltics为09:16:36行提供相同的值,但在这种情况下你只想要其中一行。

答案 1 :(得分:0)

我认为创建一个将date1翻译成date2的存储过程是有意义的,而不是将所有逻辑打包到一个查询中 - 这是可能的,但令人难以置信的丑陋和痛苦:

CREATE OR REPLACE FUNCTION date1_to_date2 (date1 IN VARCHAR2) RETURN varchar2
IS

  ts TIMESTAMP;

BEGIN

  ts := TO_TIMESTAMP(SUBSTR(date1, 1, 19), 'YYYY-MM-DD HH24:MI:SS');

  IF SUBSTR(date1, 21, 3) > 0 THEN
    ts := ts + NUMTODSINTERVAL(1, 'SECOND');
  END IF;

  ts := ts + NUMTODSINTERVAL(MOD(60 - TO_CHAR(ts, 'SS'), 6), 'SECOND');

  RETURN TO_CHAR(ts, 'YYYY-MM-DD HH24:MI:SS');

END date1_to_date2;
/

SHOW ERRORS;

其余的虽然不是很简单,但仍然很可口:

SELECT date2,
       close1 AS close2,
       volume2
  FROM ( SELECT date2,
                close1,
                ROW_NUMBER() OVER (PARTITION BY date2 ORDER BY date1 DESC) AS rn,
                SUM(volume1) OVER (PARTITION BY date2) AS volume2
           FROM ( SELECT date1,
                         date1_to_date2(date1) AS date2,
                         close1,
                         volume1
                    FROM temp2
                )
       )
 WHERE rn = 1
 ORDER BY date2
;

(实际上你可以只使用一个子查询,通过将date2的定义替换到任何地方 - 甚至可能没有任何子查询,如果WHERE rn = 1可以转换为{{1}我没有尝试的子句 - 但我认为这是最明确的方式。)