【问题标题】:Getting a sum of timestamps in oracle sql (with fractional seconds)在 oracle sql 中获取时间戳的总和(带小数秒)
【发布时间】:2018-02-03 20:28:05
【问题描述】:

您好,我试图找到一种解决方案来对一列中的时间戳求和。我找到了一些帖子,但我认为最终这些解决方案会切断小数秒。

所以我有一个时间戳格式的列,带有 6 个小数秒,例如 '0001-01-01 00:01:23.652148'

我使用此列来保存比赛中的单圈时间。有什么想法吗? :) 也许我应该使用其他数据类型或格式?

请帮忙! :) 非常感谢。

【问题讨论】:

  • 如何将时间存储为秒和小数秒。
  • 好吧,也许我可以,但我已经将它们存储在时间戳中,我想避免更改表格 :) 但假设这将是最简单的解决方案......我如何转换秒来呈现时间像这样 HH:MM:SS.FFFFFF ?
  • 0001-01-01 是不久前。时间戳是带有小数秒的日期时间,所以我不确定将它们加在一起意味着什么。
  • 在我看来,这个问题完全出在 Oracle 上。此类数据的“最正确”数据类型应该是 INTERVAL DAY TO SECOND。但是,因为诸如 SUM 之类的聚合函数还不能处理 INTERVAL 类型(嘿,Oracle?怎么了 DAT?!?)最简单的解决方案是将时间戳列转换为 NUMBER,表示秒数。

标签: sql oracle sum timestamp


【解决方案1】:

使用INTERVAL DAY TO SECOND 类型存储数据,然后使用自定义聚合函数对它们求和。 (但是,如果您必须使用 TIMESTAMP 数据类型,则可以通过减去一个纪元 TIMESTAMP 将其转换为 INTERVAL - 即 TIMESTAMP '0001-01-01 00:00:00.000000)。

SQL Fiddle

Oracle 11g R2 架构设置

CREATE TABLE LapTimes( time TIMESTAMP(6) )
/

INSERT INTO LapTimes ( time )
SELECT TIMESTAMP '0001-01-01 00:01:23.652148' FROM DUAL UNION ALL
SELECT TIMESTAMP '0001-01-01 00:01:24.123456' FROM DUAL UNION ALL
SELECT TIMESTAMP '0001-01-01 00:01:22.987654' FROM DUAL
/

自定义聚合函数

CREATE OR REPLACE TYPE IntervalAggregation AS OBJECT(
  value INTERVAL DAY(9) TO SECOND(9),

  STATIC FUNCTION ODCIAggregateInitialize(
    ctx         IN OUT IntervalAggregation
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateIterate(
    self        IN OUT IntervalAggregation,
    value       IN     INTERVAL DAY TO SECOND
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateTerminate(
    self        IN OUT IntervalAggregation,
    returnValue    OUT INTERVAL DAY TO SECOND,
    flags       IN     NUMBER
  ) RETURN NUMBER,

  MEMBER FUNCTION ODCIAggregateMerge(
    self        IN OUT IntervalAggregation,
    ctx         IN OUT IntervalAggregation
  ) RETURN NUMBER
);
/

CREATE OR REPLACE TYPE BODY IntervalAggregation
IS
  STATIC FUNCTION ODCIAggregateInitialize(
    ctx         IN OUT IntervalAggregation
  ) RETURN NUMBER
  IS
  BEGIN
    ctx := IntervalAggregation( NULL );
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateIterate(
    self        IN OUT IntervalAggregation,
    value       IN     INTERVAL DAY TO SECOND
  ) RETURN NUMBER
  IS
  BEGIN
    IF value IS NULL THEN
      NULL;
    ELSIF self.value IS NULL THEN
      self.value := value;
    ELSE
      self.value := self.value + value;
    END IF;
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateTerminate(
    self        IN OUT IntervalAggregation,
    returnValue    OUT INTERVAL DAY TO SECOND,
    flags       IN     NUMBER
  ) RETURN NUMBER
  IS
  BEGIN
    returnValue := self.value;
    RETURN ODCIConst.SUCCESS;
  END;

  MEMBER FUNCTION ODCIAggregateMerge(
    self        IN OUT IntervalAggregation,
    ctx         IN OUT IntervalAggregation
  ) RETURN NUMBER
  IS
  BEGIN
    IF self.value IS NULL THEN
      self.value := ctx.value;
    ELSIF ctx.value IS NULL THEN
      NULL;
    ELSE
      self.value := self.value + ctx.value;
    END IF;
    RETURN ODCIConst.SUCCESS;
  END;
END;
/

CREATE FUNCTION IntervalSum( value INTERVAL DAY TO SECOND )
RETURN INTERVAL DAY TO SECOND
PARALLEL_ENABLE AGGREGATE USING IntervalAggregation;
/

查询 1

SELECT IntervalSum( time - TIMESTAMP '0001-01-01 00:00:00.000000' ) AS totalLapTime
FROM   LapTimes

Results

|    TOTALLAPTIME |
|-----------------|
| 0 0:4:10.763258 |

查询 2

-- INSERT INTO YOUR_OTHER_TABLE ( TotalLapTime )
SELECT TIMESTAMP '0001-01-01 00:00:00.000000'
       + NUMTODSINTERVAL(
           SUM( 
             EXTRACT( MINUTE FROM time ) * 60
             + EXTRACT( SECOND FROM time )
           ),
           'SECOND'
         ) AS TotalLapTime
FROM   LapTimes

Results

|            TOTALLAPTIME |
|-------------------------|
| 1-01-01 00:04:10.763258 |

【讨论】:

  • 嗯。我相信你这行得通,但我认为这对我的知识水平来说太过分了。当然,我知道什么是自定义函数,我可以自己创建一个。尽管我需要一些不包含自定义功能的东西。这是因为我正在为学校做一个项目,我们还没有做过这种想法。我想出了这样的东西,但现在我卡住了,因为我不知道如何将时间间隔转换为时间戳......;/ NUMTODSINTERVAL(SUM( extract( MINUTE FROM L_TIME)*60 + extract( SECOND FROM L_TIME)), 'SECOND ')
  • 它给出了很好的数字。但下一步是我想将总和插入其他时间戳列;)
  • 对我来说很奇怪的是,Oracle 仍然没有为interval 类型重载聚合函数。我不知道是什么让他们这么久。我在 2004 年写了this,希望在下一个版本中修复它。
  • @Sławek 为什么使用TIMESTAMP?您有一个时间间隔,因此您应该使用 INTERVAL 数据类型,而不是试图将其作为从 1 月 1 日 1 日开始的时间偏移。
  • @Sławek 使用替代方法进行了更新 - 提取时间的组成部分并将其全部转换为秒,然后将它们相加并使用 NUMTODSINTERVAL 将其转换为 INTERVAL DAY TO SECOND 数据类型以添加​​到一个(纪元)时间戳(然后您可以将其插入到另一个表中)。
猜你喜欢
  • 2011-04-26
  • 2017-07-24
  • 2020-07-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-22
  • 1970-01-01
  • 2016-11-22
相关资源
最近更新 更多