AVG and SUM for INTERVALs
Presumably due to an oversight, Oracle have not yet overloaded the SUM and AVG functions to support the INTERVAL DAY TO SECOND datatype. This is a pity, as it greatly reduces the usefulness of these types for database columns - it's not much use having an "elapsed time" column you can't add up, so many people resort to a plain numeric column representing times in seconds. (Please consider upvoting this suggestion on the Oracle Database Ideas forum.)
Fortunately it's not hard to write your own. Here are my AVG_DSINTERVAL and SUM_DSINTERVAL aggregate/analytic functions.
-- AVG and SUM aggregate functions for INTERVAL DAY TO SECOND values. -- William Robertson 2004, www.williamrobertson.net -- Updated 11/2012 to use DSINTERVAL_UNCONSTRAINED type for interval parameters. -- -- Since introducing the interval types in 9i, Oracle has neglected to overload the -- aggregate functions SUM, AVG etc for them: -- -- SQL> select sum(interval '1' second) from dual -- select sum(interval '1' second) from dual -- * -- ERROR at line 1: -- ORA-00932: inconsistent datatypes: expected NUMBER got INTERVAL DAY TO SECOND -- -- Until they get around to it, you can create your own: -- -- SQL> select sum_dsinterval(interval '1' second) from dual; -- -- SUM_DSINTERVAL(INTERVAL'1'SECOND) -- --------------------------------------------------------------------------- -- +000000000 00:00:01.000000000 -- -- 1 row selected. -- Cleardown: begin for r in ( select t.type_name from user_types t where t.type_name in ('DSINTERVAL_AVG', 'DSINTERVAL_SUM') ) loop execute immediate 'drop type ' || r.type_name; loop; end; / -- Each aggregate function requires a corresponding type, providing a standard interface with 4 ODCIAggregate* member functions. -- Create types for AVG and SUM: create or replace type dsinterval_avg as object ( total interval day(6) to second , items integer , static function ODCIAggregateInitialize ( sctx in out dsinterval_avg ) return number , member function ODCIAggregateIterate ( self in out dsinterval_avg , value in interval day to second ) return number , member function ODCIAggregateMerge ( self in out dsinterval_avg , ctx2 in dsinterval_avg ) return number , member function ODCIAggregateTerminate ( self in dsinterval_avg , returnvalue out dsinterval_unconstrained , flags in number ) return number ); / show errors create or replace type body dsinterval_avg as static function ODCIAggregateInitialize ( sctx in out dsinterval_avg ) return number is begin sctx := new dsinterval_avg(interval '0' second, 0); return ODCIConst.Success; end; member function ODCIAggregateIterate ( self in out dsinterval_avg , value in interval day to second ) return number is begin self.total := self.total + value; self.items := self.items + 1; return ODCIConst.Success; end; member function ODCIAggregateTerminate ( self in dsinterval_avg , returnValue out dsinterval_unconstrained , flags in number ) return number is begin returnvalue := self.total / self.items; return ODCIConst.Success; end; member function ODCIAggregateMerge ( self in out dsinterval_avg , ctx2 in dsinterval_avg ) return number is begin -- The ODCIAggregateMerge function is used to combine results of -- parallel queries. Presumably it is applied prior to DCIAggregateTerminate, -- in which case it should probably combine totals from each thread. -- Needs testing. self.total := self.total + ctx2.total; self.items := self.items + ctx2.items; return ODCIConst.Success; end; end; / show errors -- Corresponding SUM aggregate: create or replace type dsinterval_sum as object ( total interval day(6) to second , static function ODCIAggregateInitialize ( sctx in out dsinterval_sum ) return number , member function ODCIAggregateIterate ( self in out dsinterval_sum , value in interval day to second ) return number , member function ODCIAggregateTerminate ( self in dsinterval_sum , returnValue out dsinterval_unconstrained , flags in number ) return number , member function ODCIAggregateMerge ( self in out dsinterval_sum , ctx2 in dsinterval_sum ) return number ); / show errors create or replace type body dsinterval_sum as static function ODCIAggregateInitialize ( sctx in out dsinterval_sum ) return number is begin sctx := new dsinterval_sum(interval '0' second); return ODCIConst.Success; end; member function ODCIAggregateIterate ( self in out dsinterval_sum , value in interval day to second ) return number is begin self.total := self.total + value; return ODCIConst.Success; end; member function ODCIAggregateMerge ( self in out dsinterval_sum , ctx2 in dsinterval_sum ) return number is begin -- The ODCIAggregateMerge function is used to combine results of parallel queries prior to ODCIAggregateTerminate. self.total := self.total + ctx2.total; return ODCIConst.Success; end; member function ODCIAggregateTerminate ( self in dsinterval_sum , returnValue out dsinterval_unconstrained , flags in number ) return number is begin returnvalue := self.total; return ODCIConst.Success; end; end; / show errors -- Finally, create the functions: create or replace function sum_dsinterval ( input interval day to second ) return interval day to second parallel_enable aggregate using dsinterval_sum; / create or replace function avg_dsinterval ( input interval day to second ) return interval day to second parallel_enable aggregate using dsinterval_avg; / -- Examples: with tstamp as ( select timestamp '2012-11-12 12:00:00' as t1 , timestamp '2012-11-13 13:01:02' as t2 from dual ) select sum_dsinterval(i) from ( select t2 - t1 as i from tstamp ); -- Avg and Sum of timestamp differences with times as ( select timestamp '2012-11-12 12:00:00' as t1 , timestamp '2012-11-13 13:01:02' as t2 from dual union all select timestamp '2012-11-12 12:34:56' as t1 , timestamp '2012-11-13 14:56:08' as t2 from dual ) select avg_dsinterval(t2 - t1) as average_duration , sum_dsinterval(t2 - t1) as total_duration from times; -- Analytic demo with demo (item, seq, duration) as ( select 'A', 1, interval '1' minute from dual union all select 'A', 2, interval '2' minute from dual union all select 'A', 3, interval '3' minute from dual union all select 'B', 1, interval '4' minute from dual union all select 'B', 2, interval '5' minute from dual union all select 'B', 3, interval '6' minute from dual ) select item, seq , cast(duration as interval day(1) to second(0)) as duration , cast(sum_dsinterval(duration) over(partition by item order by seq) as interval day(1) to second(0)) as duration_running_total from demo;