不幸的是,Oracle 不支持大多数间隔函数。虽然有许多解决方法,但它们都有某种缺点(特别是没有符合 ANSI-SQL 的解决方案)。
最好的答案(正如 @justsalt 后来发现的那样)是编写一个自定义函数将间隔转换为数字,对数字求平均值,然后(可选地)将其转换回间隔。Oracle 12.1 及更高版本支持使用 WITH
块声明函数:
with
function fn_interval_to_sec(i in dsinterval_unconstrained)
return number is
begin
return ((extract(day from i) * 24
+ extract(hour from i) )*60
+ extract(minute from i) )*60
+ extract(second from i);
end;
select numtodsinterval(avg(fn_interval_to_sec(endtime-starttime)), 'SECOND')
from timings;
如果您使用的是11.2或更早版本,或者您不想在SQL语句中包含函数,您可以将其声明为存储函数:
create or replace function fn_interval_to_sec(i in dsinterval_unconstrained)
return number is
begin
return ((extract(day from i) * 24
+ extract(hour from i) )*60
+ extract(minute from i) )*60
+ extract(second from i);
end;
然后您可以像预期的那样在SQL中使用它:
select numtodsinterval(avg(fn_interval_to_sec(endtime-starttime)), 'SECOND')
from timings;
使用dsinterval_unconstrained
在函数参数中使用PL/SQL类型别名dsinterval_unconstrained
可以确保您具有最大的精度/比例;INTERVAL DAY TO SECOND
将DAY
精度默认为2位数(意味着任何超过±100天的内容都会溢出并抛出异常),将SECOND
比例设置为6位数。
此外,Oracle 12.1将在您尝试指定任何精度/比例时引发PL/SQL错误:
with
function fn_interval_to_sec(i in interval day(9) to second(9))
return number is
...
ORA-06553: PLS-103: 遇到符号"("时,期望以下一种:to
备选方案
自定义聚合函数
Oracle支持使用PL/SQL编写自定义聚合函数,这将允许您对语句进行最小的更改:
select ds_avg(endtime-starttime) from timings;
然而,这种方法有几个主要缺点:
- 您必须在数据库中创建PL/SQL聚合对象,这可能不被期望或允许;
- 您不能将其命名为
avg
,因为Oracle将始终使用内置的avg
函数而不是您自己的函数。 (从技术上讲,您可以这样做,但那么您就必须用架构来限定它,这与初衷相违背。)
- 正如@vadzim指出的那样,聚合PL/SQL函数具有显着的性能开销。
日期算术运算
如果您的值之间没有显着差异,则@vadzim的方法同样适用:
select avg((sysdate + (endtime-starttime)*24*60*60*1000000 - sysdate)/1000000.0)
from timings;
请注意,如果时间间隔太大,
(endtime-starttime)*24*60*60*1000000
表达式将会溢出并抛出
ORA-01873: the leading precision of the interval is too small
错误。在这个精度(1微秒)下,差异的大小不能超过或等于
00:16:40
,因此对于小间隔是安全的,但不是所有情况都适用。
最后,如果您可以接受失去所有亚秒精度,您可以将
TIMESTAMP
列转换为
DATE
;从
DATE
中减去一个
DATE
将返回具有秒精度的天数(感谢@jimmyorr)。
select avg(cast(endtime as date)-cast(starttime as date))*24*60*60
from timings;
24*60*60 = 86400
,然后加到日期上,得到的结果将是一个日期并丢失任何小数秒 - 因此,如果时间戳精确到微秒(或任何小于1/86400秒的时间单位),则会失去精度。 - MT0(sysdate + (end_ts - start_ts)*24*60*60*1000000 - sysdate)/1000000.0
实现TIMESTAMP(9)的纳秒精度。 - Vadzim