calculate the running total over the column contain date difference in HH:MI:SS format in oracle - oracle

I have to find the running total over the column interval.
SELECT
( ( EXTRACT(DAY FROM intrvl) * 24 ) + ( EXTRACT(HOUR FROM intrvl) ) ) ||':'||
EXTRACT(MINUTE FROM intrvl) ||':'||
EXTRACT(SECOND FROM intrvl) ||':'|| as interval
FROM
(
SELECT
( to_timestamp(TO_CHAR(date_column_name,'dd-mon-rrrr hh:mi:ss') ) ) - ( to_timestamp(TO_CHAR(date_column_name,'dd-mon-rrrr hh:mi:ss') ) ) intrvl
FROM
dual
);
currrently Interval column of table has below data:
Interval(HH:mi:ss)
0:4:23
696:1:36
696:4:51
8760:1:18

The best I can come up with is this. Note that the interval data type does not take a format model for displaying - you can't force an interval of 25 hours to be displayed as 25:00:00 (although you can use that to INPUT an interval). Instead, it will be shown as 01 01:00:00 (meaning, a day and an hour).
with
tbl (interv) as (
select interval '0:4:23' hour(9) to second from dual union all
select interval '696:1:36' hour(9) to second from dual union all
select interval '696:4:51' hour(9) to second from dual union all
select interval '8760:1:18' hour(9) to second from dual
)
select interval '1' day * sum(date '2000-01-01' + interv - date'2000-01-01')
as sum_interv
from tbl;
SUM_INTERV
--------------------
+423 00:12:08.000000
In your original attempt you were trying to get a STRING output. I am not sure that's wise, but if that's what you need you can do it like so:
with
tbl (interv) as (
select interval '0:4:23' hour(9) to second from dual union all
select interval '696:1:36' hour(9) to second from dual union all
select interval '696:4:51' hour(9) to second from dual union all
select interval '8760:1:18' hour(9) to second from dual
)
, prep (sum_interv) as (
select interval '1' day * sum(date '2000-01-01' + interv - date'2000-01-01')
from tbl
)
select to_char( extract(day from sum_interv) * 24
+ extract(hour from sum_interv), 'fm999999999' ) || ':' ||
to_char( extract(minute from sum_interv), 'fm00' ) || ':' ||
to_char( extract(second from sum_interv), 'fm00' ) as sum_interv
from prep
;
SUM_INTERV
------------------
10152:12:08

Related

How to give a client's state by time

Table t_customer_statistics
trx_date - transaction date
cuid - id person(divide prospect and client)
lifecycle_status - this column must be filled
I need to give status to a client based on his condition
acquired - this month was the very first transaction
existing - there was a transaction last month
reactivated - there was no transaction last month
sleeping - there has been no transaction for the last 90 days (there have been no subsequent ones since the last transaction, more than 90 days)
I roughly made a code like this
UPDATE t_customer_statistics
SET Lifecycle_status =
case
when to_char (trunc (trx_date, 'mm'), 'mm.yyyy') = to_char (trunc (sysdate, 'mm'), 'mm.yyyy') then 'acquired'
when to_char (trunc (trx_date, 'mm'), 'mm.yyyy') = to_char (trunc (sysdate, 'mm') - 1, 'mm.yyyy') then 'existing'
when to_char (trunc (trx_date, 'mm'), 'mm.yyyy') = to_char (trunc (sysdate, 'mm') - 40, 'mm.yyyy') then 'reactivated'
when to_char (trunc (trx_date, 'mm'), 'mm.yyyy') <to_char (trunc (sysdate, 'mm') - 90, 'mm.yyyy') then 'sleeping'
end
but when they gave me an example, if the client made the first transaction and then fell asleep, then he has two states in the end, and sleeping must be separated so that there is a separate
PS. must be considered by transaction from the first and last
You could use a MERGE statement like this:
MERGE INTO clients dst
USING (
SELECT rowid rid,
LEAD(dt, 1) OVER (PARTITION BY id ORDER BY dt DESC) AS prev_dt,
LAG(dt, 1) OVER (PARTITION BY id ORDER BY dt DESC) AS next_dt
FROM clients
) src
ON ( dst.ROWID = src.rid )
WHEN MATCHED THEN
UPDATE
SET status = CASE
WHEN prev_dt IS NULL
THEN 'acquired'
WHEN MONTHS_BETWEEN(TRUNC(dst.dt, 'MM'), TRUNC(src.prev_dt)) <= 1
THEN 'existing'
ELSE 'reactivated'
END
||
CASE
WHEN COALESCE(src.next_dt, SYSDATE) >= dst.dt + INTERVAL '90' DAY
THEN ', sleeping'
END;
Which, for the sample data:
CREATE TABLE clients (id, dt, status ) AS
SELECT 1, DATE '2020-01-01', CAST( NULL AS VARCHAR2(20) ) FROM DUAL UNION ALL
SELECT 1, DATE '2020-02-01', CAST( NULL AS VARCHAR2(20) ) FROM DUAL UNION ALL
SELECT 1, DATE '2020-03-01', CAST( NULL AS VARCHAR2(20) ) FROM DUAL UNION ALL
SELECT 1, DATE '2020-05-01', CAST( NULL AS VARCHAR2(20) ) FROM DUAL UNION ALL
SELECT 1, DATE '2020-09-01', CAST( NULL AS VARCHAR2(20) ) FROM DUAL UNION ALL
SELECT 1, DATE '2020-10-01', CAST( NULL AS VARCHAR2(20) ) FROM DUAL UNION ALL
SELECT 1, DATE '2020-10-01' + INTERVAL '91' DAY, CAST( NULL AS VARCHAR2(20) ) FROM DUAL;
Then the result of the MERGE is:
ID
DT
STATUS
1
01-JAN-20
acquired
1
01-FEB-20
existing
1
01-MAR-20
existing
1
01-MAY-20
reactivated, sleeping
1
01-SEP-20
reactivated
1
01-OCT-20
existing, sleeping
1
31-DEC-20
reactivated, sleeping
db<>fiddle here

Sum totals of timestamp

I have the following query to calculate the summation timestamp
SELECT SUM(TIME_SPENT) FROM
(
select a - b as time_spent from tbl1 where name = 'xxx'
union all
select c - d as time_spent from tbl2 where name= 'yyy'
)a;
The sub-query return result as
+00 00:01:54.252000
But the entire query return error as ORA-00932: inconsistent datatypes: expected NUMBER got INTERVAL DAY TO SECOND.
Understand it required something like this
SELECT COALESCE (
(to_timestamp('2014-09-22 16:00:00','yyyy/mm/dd HH24:MI:SS') - to_timestamp('2014-09-22 09:00:00','yyyy/mm/dd HH24:MI:SS')) -
(to_timestamp('2014-09-22 16:00:00','yyyy/mm/dd HH24:MI:SS') - to_timestamp('2014-09-22 09:00:00','yyyy/mm/dd HH24:MI:SS')), INTERVAL '0' DAY) FROM DUAL;
How can achieve along with sub-queries that retrieve data from Timestamp type columns?
You cannot sum INTERVAL DAY TO SECOND in Oracle. I think this is one of the top rated open feature request.
You can cast the TIMESTAMP into DATE values, then the result is the difference in days. Multiply by 24*60*60 is you like to get seconds instead:
SELECT SUM(TIME_SPENT) * 24*60*60 FROM FROM
(
select CAST(a AS DATE) - CAST(b AS DATE) as time_spent from tbl1 where name = 'xxx'
union all
select CAST(d AS DATE) - CAST(d AS DATE) as time_spent from tbl2 where name= 'yyy'
);
Or you can write a function which converts INTERVAL DAY TO SECOND into seconds:
CREATE OR REPLACE FUNCTION GetSeconds(ds INTERVAL DAY TO SECOND) DETERMINISTIC RETURN NUMBER AS
BEGIN
RETURN EXTRACT(DAY FROM ds)*24*60*60
+ EXTRACT(HOUR FROM ds)*60*60
+ EXTRACT(MINUTE FROM ds)*60
+ EXTRACT(SECOND FROM ds);
END;
and use it like this:
SELECT SUM(TIME_SPENT), numtodsinterval(SUM(TIME_SPENT), 'second')
(
select GetSeconds(a-b) as time_spent from tbl1 where name = 'xxx'
union all
select GetSeconds(c-d) as time_spent from tbl2 where name= 'yyy'
);
try using below query
SELECT sum(extract(second from time_spent)) FROM
(
select a - b as time_spent from test2 where name = 'xxx'
union all
select c - d as time_spent from tbl2 where name= 'yyy'
)a;
Looks like time_spent column is timestamp type in your table an d it is not able to pass the correct data type in Sum function. Use extract function to get Seconds from time_spent.
with t(a,b) as (
select timestamp'2014-09-22 16:00:00.000', timestamp'2014-09-23 16:00:00.001' from dual union all
select timestamp'2014-09-22 16:00:00.000', timestamp'2014-09-24 16:00:00.001' from dual union all
select timestamp'2014-09-22 16:00:00.000', timestamp'2014-09-25 16:00:00.001' from dual union all
select timestamp'2014-09-22 16:00:00.000', timestamp'2014-09-26 16:00:00.001' from dual union all
select timestamp'2014-09-22 16:00:00.000', timestamp'2014-09-27 16:00:00.001' from dual
)
select
sum( (date'1-1-1'+(b-a)*24*60*60 - date'1-1-1')) as ssum_seconds_1,
round(sum( (date'1-1-1'+(b-a)*24*60*60 - date'1-1-1')), 3) as ssum_seconds_rounded,
numtodsinterval( round(sum( (date'1-1-1'+(b-a)*24*60*60 - date'1-1-1')), 3), 'second') dsint
from t
/

Convert time from a format to an int in Oracle

I can't seem to figure this out. I have some rows with time in the format 00:00:00 (hh:mm:ss) and i need to calculate the total time it takes for a task.
I am unable to sum this data. Can someone advise on a way to convert this to a format i can sum or a method to calculate the total time for the task.
Thanks for any assistance. This is in an Oracle DB.
Convert your time string to a date and subtract the equivalent date at midnight to give you an number as a fraction of a day. You can then sum this number and convert it to an interval:
Oracle Setup:
CREATE TABLE test_data( value ) AS
SELECT '01:23:45' FROM DUAL UNION ALL
SELECT '12:34:56' FROM DUAL UNION ALL
SELECT '23:45:00' FROM DUAL;
Query:
SELECT NUMTODSINTERVAL(
SUM( TO_DATE( value, 'HH24:MI:SS' ) - TO_DATE( '00:00:00', 'HH24:MI:SS' ) ),
'DAY'
) AS total_time_taken
FROM test_data;
Output:
| TOTAL_TIME_TAKEN |
| :---------------------------- |
| +000000001 13:43:41.000000000 |
db<>fiddle here
Update including durations longer than 23:59:59.
Oracle Setup:
CREATE TABLE test_data( value ) AS
SELECT '1:23:45' FROM DUAL UNION ALL
SELECT '12:34:56' FROM DUAL UNION ALL
SELECT '23:45:00' FROM DUAL UNION ALL
SELECT '48:00:00' FROM DUAL;
Query:
SELECT NUMTODSINTERVAL(
SUM(
DATE '1970-01-01'
+ NUMTODSINTERVAL( SUBSTR( value, 1, HM - 1 ), 'HOUR' )
+ NUMTODSINTERVAL( SUBSTR( value, HM + 1, MS - HM - 1 ), 'MINUTE' )
+ NUMTODSINTERVAL( SUBSTR( value, MS + 1 ), 'SECOND' )
- DATE '1970-01-01'
),
'DAY'
) AS total_time
FROM (
SELECT value,
INSTR( value, ':', 1, 1 ) AS HM,
INSTR( value, ':', 1, 2 ) AS MS
FROM test_data
);
Output:
| TOTAL_TIME |
| :---------------------------- |
| +000000003 13:43:41.000000000 |
db<>fiddle here
Even better would be if you changed your table to hold the durations as intervals rather than as strings then everything becomes much simpler:
Oracle Setup:
CREATE TABLE test_data( value ) AS
SELECT INTERVAL '1:23:45' HOUR TO SECOND FROM DUAL UNION ALL
SELECT INTERVAL '12:34:56' HOUR TO SECOND FROM DUAL UNION ALL
SELECT INTERVAL '23:45:00' HOUR TO SECOND FROM DUAL UNION ALL
SELECT INTERVAL '48:00:00' HOUR TO SECOND FROM DUAL;
Query:
SELECT NUMTODSINTERVAL(
SUM( DATE '1970-01-01' + value - DATE '1970-01-01' ),
'DAY'
) AS total_time
FROM test_data;
Output:
| TOTAL_TIME |
| :---------------------------- |
| +000000003 13:43:41.000000000 |
db<>fiddle here

Count the no of saturdays and sundays in date range - oracle [duplicate]

This question already has answers here:
Number of fridays between two dates
(7 answers)
Closed 8 years ago.
I have two parameters(start_Date,end_Date) from table1
I'm trying to count no of saturdays and sundays in a date range
star_Date=8/20/2014 13:52
end_Date=8/28/2014 13:52
And result should be like this
Start_Date end_date No_of_leaves
8/20/2014 13:52 8/28/2014 13:52 2
Update Section
SELECT retouch_req_time,retouch_submit_time,(
SELECT Count(*) FROM (SELECT To_char(start_date + ( LEVEL - 1 ), 'fmday') dt
FROM (WITH t AS (SELECT To_date (retouch_req_time, 'MM/DD/YYYY HH24:MI') start_date, To_date (retouch_submit_time, 'MM/DD/YYYY HH24:MI') end_date FROM TT))
CONNECT BY LEVEL <= end_date - start_date + 1) WHERE dt IN ('friday','saturday')) as worked_hours
FROM TT
You can try using hierarchical queries
WITH t
AS (SELECT To_date ('8/20/2014 13:52', 'MM/DD/YYYY HH24:MI') start_date,
To_date ('8/28/2014 13:52', 'MM/DD/YYYY HH24:MI') end_date
FROM dual)
SELECT Count(*)
FROM (SELECT To_char(start_date + ( LEVEL - 1 ), 'fmday') dt
FROM t
CONNECT BY LEVEL <= end_date - start_date + 1)
WHERE dt IN ( 'friday', 'saturday' );
RESULT
------
2
* The dates are listed by expanding the range.
* The TO_CHAR function is used to obtain the weekday
* Count everthing which is a friday or saturday
If you want to find the day wise count, then you can try
SELECT To_char(dat, 'DY'),
Count(*)
FROM (SELECT To_date ('8/20/2014 13:52', 'MM/DD/YYYY HH24:MI')
+ num dat
FROM (SELECT LEVEL - 1 num
FROM dual
CONNECT BY LEVEL <= Abs(To_date ('8/20/2014 13:52',
'MM/DD/YYYY HH24:MI') -
To_date (
'8/28/2014 13:52'
,
'MM/DD/YYYY HH24:MI')) - 1
))
WHERE To_char(dat, 'DY') IN ( 'FRI', 'SAT' )
GROUP BY To_char(dat, 'DY');
RESULTS
TO_CHAR(DAT,'DY') COUNT(*)
----------------- --------
FRI 1
SAT 1
You can calculate the number of saturdays and sundays like this:
with t(d) as (
select sysdate + level from dual connect by rownum < 10
)
select count(case when trim(to_char(d, 'DAY')) in ('SATURDAY', 'SUNDAY') then 1 end) cnt from t
CNT
---
2
If you don't have a range of dates then:
with t(a, b) as (
select sysdate a, sysdate + 10 b from dual connect by rownum < 10
), t2(d) as (
select a + level - 1 from t connect by rownum <= b - a
)
select count(case when trim(to_char(d, 'DAY')) in ('SATURDAY', 'SUNDAY') then 1 end) cnt from t2
CNT
---
2

PL/SQL: TO_CHAR show format

So I have this code here:
create or replace FUNCTION calc_length(
START_TIME IN number,
FINISH_TIME IN number
) RETURN NUMBER IS
BEGIN
RETURN ( (FINISH_TIME - START_TIME ) ;
END
And I want to show the result in the format as H:mm
I tried TO_CHAR function but it accepts a strict preset formats.
Few examples - copy, paste to see the oputput:
SELECT trunc(mydate / 3600) hr
, trunc(mod(mydate, 3600) / 60) mnt
, trunc(mod(mydate, 3600) / 60 /60) sec
FROM
(
SELECT (to_date('01/02/2013 23:00:00', 'mm/dd/yyyy hh24:mi:ss') -
to_date('01/01/2013 07:00:00', 'mm/dd/yyyy hh24:mi:ss')) * 86400 mydate
FROM dual
)
/
Select hh, mi, ss From
(
Select EXTRACT(hour From Cast(SYSDATE as timestamp)) hh,
EXTRACT(minute From Cast(SYSDATE as timestamp)) mi,
EXTRACT(second From Cast(SYSDATE as timestamp)) ss
From dual
)
/
Select start_date, end_date, time_diff,
EXTRACT(DAY FROM time_diff) days,
EXTRACT(HOUR FROM time_diff) hours,
EXTRACT(MINUTE FROM time_diff) minutes,
EXTRACT(SECOND FROM time_diff) seconds
From
(
Select start_date, end_date, end_date - start_date time_diff
From
(
Select CAST(to_date('21/02/2012 06:10:53 am', 'dd/mm/yyyy hh:mi:ss am') AS TIMESTAMP) end_date
, CAST(to_date('21/02/2012 12:05:00 am', 'dd/mm/yyyy hh:mi:ss am') AS TIMESTAMP) start_date
From dual
))
/

Resources