我有高频率滴答数据,需要从微秒到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
... | ... | ...
(date1
是VARCHAR2(28)
; close1
和volume1
都是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
之和。
答案 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}我没有尝试的子句 - 但我认为这是最明确的方式。)