2009-01-16 107 views
14

在Oracle 10g中,我有一张表,其中包含显示特定操作花费多长时间的时间戳。它有两个时间戳字段:开始时间和结束时间。我想查找这些时间戳的持续时间的平均值。我尝试:如何平均时间间隔?

select avg(endtime-starttime) from timings; 

但得到:

SQL Error: ORA-00932: inconsistent datatypes: expected NUMBER got INTERVAL DAY TO SECOND

这工作:

select 
    avg(extract(second from endtime - starttime) + 
     extract (minute from endtime - starttime) * 60 + 
     extract (hour from endtime - starttime) * 3600) from timings; 

但实在是太慢了。

任何更好的方法将间隔转换为秒数或其他方式吗?

编辑: 真正放缓这种情况的原因是我在开始时间之前有一段时间。由于某些原因,这种计算令人难以置信的缓慢。我的根本问题是通过从查询集中消除它们来解决的。我也只是定义的函数来完成这种转换更容易:

FUNCTION fn_interval_to_sec (i IN INTERVAL DAY TO SECOND) 
RETURN NUMBER 
IS 
    numSecs NUMBER; 
BEGIN 
    numSecs := ((extract(day from i) * 24 
     + extract(hour from i))*60 
     + extract(minute from i))*60 
     + extract(second from i); 
    RETURN numSecs; 
END; 

回答

4

最彻底的方法是写自己的聚合函数来做到这一点,因为它会最干净地处理这个问题(处理亚秒级分辨率等)。

事实上,这个问题在asktom.oracle.com(问题包括源代码)被问及(并回答)。

+1

请注意[自定义pl/sql函数具有显着的性能开销](https://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:60122715103602)可能不是适合重度查询。到目前为止, – Vadzim 2015-08-13 09:59:57

2

它看起来不像有任何功能的甲骨文做INTERVAL DAY TO SECOND明确转化为NUMBER。请参阅this document末尾的表格,这意味着不存在此类转换。

其他来源似乎表明您正在使用的方法是从INTERVAL DAY TO SECOND数据类型获取数字的唯一方法。

,你可以在这个特定的情况下,尝试将减去之前转换为数字,但自认为会做两倍多extract离子,它很可能是唯一的其他东西更慢:

select 
    avg(
     (extract(second from endtime) + 
     extract (minute from endtime) * 60 + 
     extract (hour from endtime) * 3600) - 
     (extract(second from starttime) + 
     extract (minute from starttime) * 60 + 
     extract (hour from starttime) * 3600) 
    ) from timings; 
1

好吧,这是一个非常快速和肮脏的方法,但是如何将秒差存储在单独的列中(如果记录更改,您需要使用触发器或手动更新)并在该列上求平均值?

+1

如果你想这样做,你可以使用,为您节省一列的触发或手动更新功能基于指数(FBI) 。 fbi可以用在where子句中,也可以用在select子句中。 – tuinstoel 2009-01-16 15:31:48

9

如果您的结束时间和开始时间不是海誓山盟的一秒钟内,你可以投你的时间戳为日期和做日期计算:

select avg(cast(endtime as date)-cast(starttime as date))*24*60*60 
    from timings; 
+0

这将在时间戳中丢失任何小数秒(不管它们是否在彼此的秒内)。 – MT0 2017-12-07 10:06:30

16

有一个更短,更快,更好的方式比多毛提取物的毛茸茸的公式。减去时间标记时会秒钟

(sysdate + (endtime - starttime)*24*60*60 - sysdate) 

它还保留小数部分:

刚刚尝试这一点得到响应时间以秒计。

有关详细信息,请参阅http://kennethxu.blogspot.com/2009/04/converting-oracle-interval-data-type-to.html


请注意,custom pl/sql functions have significant performace overhead可能不适合大量查询。

+1

看起来是最简单的解决方案。如果Oracle能够为此创建一个正常的功能,那将会很好。 – 2014-02-07 18:50:20

0

SQL Fiddle

的Oracle 11g R2架构设置

CREATE TYPE IntervalAverageType AS OBJECT(
    total INTERVAL DAY(9) TO SECOND(9), 
    ct INTEGER, 

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

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

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

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

CREATE OR REPLACE TYPE BODY IntervalAverageType 
IS 
    STATIC FUNCTION ODCIAggregateInitialize(
    ctx   IN OUT IntervalAverageType 
) RETURN NUMBER 
    IS 
    BEGIN 
    ctx := IntervalAverageType(INTERVAL '0' DAY, 0); 
    RETURN ODCIConst.SUCCESS; 
    END; 

    MEMBER FUNCTION ODCIAggregateIterate(
    self  IN OUT IntervalAverageType, 
    value  IN  INTERVAL DAY TO SECOND 
) RETURN NUMBER 
    IS 
    BEGIN 
    IF value IS NOT NULL THEN 
     self.total := self.total + value; 
     self.ct := self.ct + 1; 
    END IF; 
    RETURN ODCIConst.SUCCESS; 
    END; 

    MEMBER FUNCTION ODCIAggregateTerminate(
    self  IN OUT IntervalAverageType, 
    returnValue OUT INTERVAL DAY TO SECOND, 
    flags  IN  NUMBER 
) RETURN NUMBER 
    IS 
    BEGIN 
    IF self.ct = 0 THEN 
     returnValue := NULL; 
    ELSE 
     returnValue := self.total/self.ct; 
    END IF; 
    RETURN ODCIConst.SUCCESS; 
    END; 

    MEMBER FUNCTION ODCIAggregateMerge(
    self  IN OUT IntervalAverageType, 
    ctx   IN OUT IntervalAverageType 
) RETURN NUMBER 
    IS 
    BEGIN 
    self.total := self.total + ctx.total; 
    self.ct := self.ct + ctx.ct; 
    RETURN ODCIConst.SUCCESS; 
    END; 
END; 
/

然后你就可以创建自定义聚合函数:

执行自定义聚集当创建一个类型使用

CREATE FUNCTION AVERAGE(difference INTERVAL DAY TO SECOND) 
RETURN INTERVAL DAY TO SECOND 
PARALLEL_ENABLE AGGREGATE USING IntervalAverageType; 
/

查询1

WITH INTERVALS(diff) AS (
    SELECT INTERVAL '0' DAY FROM DUAL UNION ALL 
    SELECT INTERVAL '1' DAY FROM DUAL UNION ALL 
    SELECT INTERVAL '-1' DAY FROM DUAL UNION ALL 
    SELECT INTERVAL '8' HOUR FROM DUAL UNION ALL 
    SELECT NULL FROM DUAL 
) 
SELECT AVERAGE(diff) FROM intervals 

Results

| AVERAGE(DIFF) | 
|---------------| 
|  0 2:0:0.0 |