我有一个表,其中有一列名为duration
. 它的数据类型是VARCHAR2
.
我想对列求和duration
。
00:56:30
02:08:40
01:01:00
总计=>04:05:10
如何使用 ANSI SQL 或 Oracle SQL 做到这一点?
您可以使用 分隔小时、分钟和秒SUBSTR
,然后SUM
向上,最后使用NUMTODSINTERVAL
函数将其转换为INTERVAL
类型。
SELECT NUMTODSINTERVAL (SUM (total_secs), 'second')
FROM (SELECT SUBSTR (duration, 1, 2) * 3600
+ SUBSTR (duration, 4, 2) * 60
+ SUBSTR (duration, 7, 2) total_secs
FROM user_tab);
我认为最好将您的字符串转换为INTERVAL
第一个,并将这些值添加为日期值。类似于以下内容:
select to_dsinterval('0 00:56:30')
+ to_dsinterval('0 02:08:40')
+ to_dsinterval('0 01:01:00') myinterval from dual;
MYINTERVAL
-------------------
+000000000 04:06:10
select
numtodsinterval(sum(
to_char(to_date(duration, 'HH24:MI:SS'), 'HH24') * 3600 +
to_char(to_date(duration, 'HH24:MI:SS'), 'MI') * 60+
to_char(to_date(duration, 'HH24:MI:SS'), 'SS')
), 'second'
) as SUMTOTAL
from tbl;
第二次查询
select
numtodsinterval(hr+mn+sc, 'second')
from
(
select
sum(to_char(to_date(duration, 'HH24:MI:SS'), 'HH24') * 3600) as hr,
sum(to_char(to_date(duration, 'HH24:MI:SS'), 'MI') * 60) as mn,
sum(to_char(to_date(duration, 'HH24:MI:SS'), 'SS'))as sc
from tbl) tmp
在 SQL FIDDLE 中返回对象的示例。在你的机器上试试
试试这个
select sec_to_time(sum(time_to_sec(duration))) from tbl
| SEC_TO_TIME(SUM(TIME_TO_SEC(DURATION))) |
-------------------------------------------
| January, 01 1970 04:06:10+0000 |
为了好玩,我编写了自己的聚合函数,可以对区间求和(参见@Yasir 的帖子)。这可以修改为在内部进行 varchar 到间隔转换,但我现在会尽可能简单)。
首先创建对象类型规范:
CREATE OR REPLACE TYPE SumInterval
AS OBJECT (
runningSum INTERVAL DAY(9) TO SECOND(9),
STATIC FUNCTION ODCIAggregateInitialize
( actx IN OUT SumInterval
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateIterate
( self IN OUT SumInterval,
val IN DSINTERVAL_UNCONSTRAINED
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateTerminate
( self IN SumInterval,
returnValue OUT DSINTERVAL_UNCONSTRAINED,
flags IN NUMBER
) RETURN NUMBER,
MEMBER FUNCTION ODCIAggregateMerge
(self IN OUT SumInterval,
ctx2 IN SumInterval
) RETURN NUMBER
);
和对象主体:
CREATE OR REPLACE TYPE BODY SumInterval AS
STATIC FUNCTION ODCIAggregateInitialize
( actx IN OUT SumInterval
) RETURN NUMBER IS
BEGIN
IF actx IS NULL THEN
actx := SumInterval (INTERVAL '0 0:0:0.0' DAY TO SECOND);
ELSE
actx.runningSum := INTERVAL '0 0:0:0.0' DAY TO SECOND;
END IF;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateIterate
( self IN OUT SumInterval,
val IN DSINTERVAL_UNCONSTRAINED
) RETURN NUMBER IS
BEGIN
self.runningSum := self.runningSum + val;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateTerminate
( self IN SumInterval,
ReturnValue OUT DSINTERVAL_UNCONSTRAINED,
flags IN NUMBER
) RETURN NUMBER IS
BEGIN
returnValue := self.runningSum;
RETURN ODCIConst.Success;
END;
MEMBER FUNCTION ODCIAggregateMerge
(self IN OUT SumInterval,
ctx2 IN SumInterval
) RETURN NUMBER IS
BEGIN
self.runningSum := self.runningSum + ctx2.runningSum;
RETURN ODCIConst.Success;
END;
END;
最后是使用此对象类型的函数:
CREATE OR REPLACE FUNCTION sum_interval( x DSINTERVAL_UNCONSTRAINED)
RETURN DSINTERVAL_UNCONSTRAINED PARALLEL_ENABLE
AGGREGATE USING SumInterval;
现在您可以使用新的“sum_interval”,如下所示:
with x as (
select to_dsinterval('0 00:56:30') as duration from dual
union
select to_dsinterval('0 02:08:40') as duration from dual
union
select to_dsinterval('0 01:01:00') as duration from dual
)
select sum_interval(duration)
from x;
输出:
SUM_INTERVAL(DURATION)
+00 04:06:10.000000
这样做的另一个好处是它可以像典型的聚合函数一样使用。例如,我们可以将其用作组函数:
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, sum_interval(duration)
from x
group by state;
输出:
STATE SUM_INTERVAL(DURATION)
FL +00 03:05:10.000000
GA +00 01:01:00.000000