【问题标题】:Using Oracle, from microseconds to 6 seconds interval使用 Oracle,从微秒到 6 秒的间隔
【发布时间】:2012-02-18 13:57:46
【问题描述】:

我有高频滴答数据,需要从微秒到 6 秒的间隔进行排序。每日分时数据从美国东部标准时间 09:15:00.000 开始,到美国东部标准时间 15:15:00.000 结束。

这是我的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 都是 NUMBERs)。

我如何得到以下结果?

 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之和。

【问题讨论】:

  • 我不明白你的输入数据和你的输出之间的关系;你能详细说明一下吗?
  • 如果您要存储时间戳,然后将它们存储在 timestamp 数据类型中,那么之后排序很容易......
  • @ruakh:close2 列使用间隔 00、06、12、18、24、30、36、42、48、54 的 last_value。如果微秒在 00-05.999 范围内,只需设置 close2列值作为区间的last_vlue,设置volume2列值作为区间的volume1之和。
  • 您可以使用 to_timestamp 将 varchar2 字段转换为时间戳数据类型
  • 工作压力大,急需专家帮助。继续寻找。

标签: sql oracle intervals


【解决方案1】:

问题的核心是将您拥有的时间戳转换为与您的 6 秒间隔相对应的“桶”。一种方法是提取到分钟的日期部分,这可以使用TRUNC 函数完成,并将秒数作为单独的值。 (请注意,我将 date1 视为已经是 TIMESTAMP,正如 cmets 中的建议和您之前的问题的答案,但如果它仍然是 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 函数(在排序中带有desc)和SUM 的分析版本获得最新的close1 值,以获得volume1 的总数窗户。 distinct 之所以存在是因为 anayltics 为 09:16:36 的两行提供了相同的值,但在这种情况下您只需要其中一个。

【讨论】:

  • 你是如此有才华和聪明,感谢你的热心帮助。一个很好的解决方案。
  • Alex Poole,我的两个问题已由您和其他专家解决,我现在很高兴。谢谢你花这么多时间研究我的问题。来自我的问候。
  • 有趣。我第一次听说extract 函数。
【解决方案2】:

与其将所有逻辑打包到一个查询中(这是可能的,但非常丑陋和痛苦),我认为创建一个将您的 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 可以转换为HAVING 子句,我没试过——但我认为这是最清晰的方法。)

【讨论】:

  • 感谢您的帮助,Rurkh,您修改的问题中的新内容排列整齐美观。您的精彩作品为我提供了 Oracle 的深刻而新的一面。
猜你喜欢
  • 1970-01-01
  • 2012-02-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-08-01
  • 1970-01-01
  • 2017-06-24
  • 1970-01-01
相关资源
最近更新 更多