Fazer intervalos de tempo e, especialmente, fazer agregações (somas, médias) em muitos intervalos ou grupos de intervalos não é simples no Oracle. A função AVG não funciona em intervalos de timestamp, ela espera números. Então, precisaremos criar nosso próprio objeto de agregação e função que fará isso.
Primeiro, a especificação do objeto :
CREATE OR REPLACE TYPE AvgInterval
AS OBJECT (
runningSum INTERVAL DAY(9) TO SECOND(9),
runningCnt number,
STATIC FUNCTION ODCIAggregateInitialize
( actx IN OUT AvgInterval
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateIterate
( self IN OUT AvgInterval,
val IN DSINTERVAL_UNCONSTRAINED
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateTerminate
( self IN AvgInterval,
returnValue OUT DSINTERVAL_UNCONSTRAINED,
flags IN NUMBER
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateMerge
(self IN OUT AvgInterval,
ctx2 IN AvgInterval
) RETURN NUMBER
);
E o corpo do objeto :
CREATE OR REPLACE TYPE BODY AvgInterval AS
STATIC FUNCTION ODCIAggregateInitialize
( actx IN OUT AvgInterval
) RETURN NUMBER IS
BEGIN
IF actx IS NULL THEN
actx := AvgInterval (INTERVAL '0 0:0:0.0' DAY TO SECOND, 0);
ELSE
actx.runningSum := INTERVAL '0 0:0:0.0' DAY TO SECOND;
actx.runningCnt := 0;
END IF;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateIterate
( self IN OUT AvgInterval,
val IN DSINTERVAL_UNCONSTRAINED
) RETURN NUMBER IS
BEGIN
self.runningSum := self.runningSum + val;
self.runningCnt := self.runningCnt + 1;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateTerminate
( self IN AvgInterval,
ReturnValue OUT DSINTERVAL_UNCONSTRAINED,
flags IN NUMBER
) RETURN NUMBER IS
BEGIN
if (runningCnt <> 0) then
returnValue := (self.runningSum/runningCnt);
else
returnValue := self.runningSum;
end if;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateMerge
(self IN OUT AvgInterval,
ctx2 IN AvgInterval
) RETURN NUMBER IS
BEGIN
self.runningSum := self.runningSum + ctx2.runningSum;
self.runningCnt := self.runningCnt + ctx2.runningCnt;
RETURN ODCIConst.Success;
END;
END;
Finalmente, a função que usa o objeto acima :
CREATE OR REPLACE FUNCTION avg_interval( x DSINTERVAL_UNCONSTRAINED)
RETURN DSINTERVAL_UNCONSTRAINED PARALLEL_ENABLE
AGGREGATE USING AvgInterval;
Agora, podemos usar assim :
with x as (
select systimestamp - 1/24 as created_date, systimestamp as modified_date from dual
union
select systimestamp - 2/24 as created_date, systimestamp as modified_date from dual
union
select systimestamp - 3/24 as created_date, systimestamp as modified_date from dual
)
select avg_interval(modified_date - created_date)
from x;
Saída:
+00 02:00:00.562669
Também podemos agregar sobre grupos com isso :
with x as (
select 'FL' as state, to_dsinterval('0 00:56:30') as duration from dual
union
select 'FL' as state, to_dsinterval('0 02:08:40') as duration from dual
union
select 'GA' as state, to_dsinterval('0 01:01:00') as duration from dual
)
select state, avg_interval(duration)
from x
group by state;
Saída:
FL +00 01:32:35.000000
GA +00 01:01:00.000000