duration between two columns with format hhmmss - sql

How can I determine the difference / duration between two columns, which have the following format:
HHMMSS.
Leading zeros are swallowed by the system (Oracle DB).
0 o'clock is a "0".
3 o'clock is "300".
23:50 clock is "235000".
Whatever, 24:00 is also reported as "240000".
That means I have the column "Start" and the column "End".
How can you determine the duration using SQL?

Convert the values to seconds using arithmetic:
select hhmmss, floor(hhmmss / 10000) * 60 * 60 + (mod(hhmmss, 10000) / 100) * 60 + mod(hhmmss, 100)
from (select 235000 as hhmmss from dual) x
Then subtract the seconds. That will give you the duration in seconds.
With your column names, this would look like:
select ( (floor(end / 10000) * 60 * 60 + (mod(end, 10000) / 100) * 60 + mod(end, 100)) -
(floor(start / 10000) * 60 * 60 + (mod(start, 10000) / 100) * 60 + mod(start, 100))
) as diff_seconds
To convert this back to a string, you can use to_char():
select to_char(date '2000-01-01' + diff_seconds * interval '1' second, 'HH24:MI:SS')

You can achieve the duration in hour/minutes/seconds using to_date as following:
Select round(
(to_date(lpad(end,6,'0'),'HHMISS')
- to_date(lpad(start,6,'0'),'HHMISS') ) * 24
, 2) diff_in_hours
-- multiply it by another 60 to get diff in minutes (24*60)
-- multiply it by another 3600 to get diff in seconds (24*3600)
from your_table
To get the output in HHMISS just add it to sysdate
Select
TO_CHAR(Trunc(sysdate) + (to_date(lpad(end,6,'0'),'HHMISS')
- to_date(lpad(start,6,'0'),'HHMISS')) , 'HH:MI:SS') as diff_HHMISS
from your_table
Cheers!!

Related

Hourly average for timestamps columns - FROM keyword not found where expected

I would like to get the hourly average of two columns. The type of the columns is timestamp.
I try this:
select trunc(endtime, 'HH') as Date,
avg(extract(second from intrvl)
+ extract(minute from intrvl) * 60
+ extract(hour from intrvl) * 60 * 60
+ extract(day from intrvl) * 60 * 60 * 24) as average
from (
select (endtime - starttime) intrvl
from mytable
)
group by Date;
I do not know if this is the right way to go at all. But the query is not correct. I get this error: 00923. 00000 - "FROM keyword not found where expected"
In the end I would like to get a table like this one:
Date average
16/11/2021 08:00 5.786
16/11/2021 09:00 8.996
How can I get this?
You need to include all the columns you are going to use in the outer-query in the SELECT clause of the inner-query and you cannot use an alias of the SELECT clause in the GROUP BY clause in the same sub-query:
select trunc(endtime, 'HH') as "Date",
avg(
extract(second from intrvl)
+ extract(minute from intrvl) * 60
+ extract(hour from intrvl) * 60 * 60
+ extract(day from intrvl) * 60 * 60 * 24
) as average
from (
select endtime,
endtime - starttime AS intrvl
from mytable
)
group by trunc(endtime, 'HH');
If you do not have (or are not worried about) fractional seconds then you could also use:
select trunc(endtime, 'HH') as "Date",
AVG(
(CAST(endtime AS DATE) - CAST(starttime AS DATE))
* 60 * 60 * 24
) AS average
from mytable
group by trunc(endtime, 'HH');
db<>fiddle here
Date is reserved word (for a datatype), you can't use it as an alias unless you enclose it into double quotes as
SELECT TRUNC (endtime, 'HH') AS "Date",
AVG (
EXTRACT (SECOND FROM intrvl)
+ EXTRACT (MINUTE FROM intrvl) * 60
+ EXTRACT (HOUR FROM intrvl) * 60 * 60
+ EXTRACT (DAY FROM intrvl) * 60 * 60 * 24) AS average
FROM (SELECT (endtime - starttime) intrvl FROM mytable)
GROUP BY "Date";
Alternatively, rename it to e.g. c_date
SELECT TRUNC (endtime, 'HH') AS c_date,

How to get epoch function in Oracle

select *
from sample_table scr
where extract('epoch' from systimestamp - scr.created_date)/60 > :defaultTimeOut
This is a postgres query. Trying to convert this query into oracle.
How do I convert epoch in oracle?
TIA.
You are actually trying to convert an interval (ie the difference between two dates) to a number of seconds. Assuming that created_date is of date datatype, one method is:
select *
from sample_table
where (sysdate - created_date) * 24 * 60 * 60 > :defaultTimeOut
Rationale: in Oracle, substracting two dates returns a decimal number that represents the difference in days. You can multiply that by 24 (hours per day), 60 (minutes per hour) and 60 (seconds per minute) to convert that to a number of seconds, then compare it to the target value.
If created_date is of timestamp datatype, you can cast it to a date first:
select *
from sample_table
where (sysdate - cast(created_date as date)) * 24 * 60 * 60 > :defaultTimeOut

Oracle epoch date function for the previous month

I have my table with EPOCH date system, i want to select data from previous month only. I have googled a lot but the only results i find is related to datetime system. Can you please help?
I will use this SQL Query to import data to Power BI.
You can convert your date range to an epoch using ( date_to_convert - DATE '1970-01-01' ) * 24 * 60 * 60 (assuming your epoch is in seconds since 1970).
You want to find values that are greater-than-or-equal-to the start of the previous month and before the start of the current month:
SELECT *
FROM your_table
WHERE epoch_column >= ( ADD_MONTHS( TRUNC(SYSDATE,'MM'), - 1 ) - DATE '1970-01-01' ) * 24 * 60 * 60
AND epoch_column < ( TRUNC( SYSDATE, 'MM' ) - DATE '1970-01-01' ) * 24 * 60 * 60
If you use midnight of the last day of the previous month as your upper bound then you will miss all values that are on that last day but are after midnight.
You can convert a unix epoch (number of seconds since January 1st, 1970) to a date as follows :
TO_DATE('01/01/1970', 'dd/mm/yyyy') + epoch_column / 60 / 60 / 24
The division turns the epoch into a number of days, that can be added to the original date.
If you want to filter this on the previous month, then :
TO_DATE('01/01/1970', 'dd/mm/yyyy') + epoch_column / 60 / 60 / 24
BETWEEN ADD_MONTHS(TRUNC(sysdate, 'mm'), -1)
AND LAST_DAY(ADD_MONTHS(TRUNC(sysdate, 'mm'), -1))
Or better yet :
epoch_column
BETWEEN
(
ADD_MONTHS(TRUNC(sysdate, 'mm'), -1)
- TO_DATE('01/01/1970', 'dd/mm/yyyy')
) * 60 * 60 * 24
AND (
LAST_DAY(ADD_MONTHS(TRUNC(sysdate, 'mm'), -1))
- TO_DATE('01/01/1970', 'dd/mm/yyyy')
) * 60 * 60 * 24
This solution should be more efficient, as no operation is performed on the column being filtered, hence it should be able to take advantage of an existing index on epoch_column.

How to convert milliseconds to Time(hh:mm:ss) in Oracle

I would like to convert in PL/SQL miliseconds to Time(hh:mm:ss)
21649000 to 06:00:49
or
83293000 to 23:08:13
Use NUMTODSINTERVAL with SECOND option :
select NUMTODSINTERVAL( 83293000 / 1000, 'SECOND' ) "Time" from dual t;
Time
-------------------
+000000000 23:08:13
This is a quite self-explanatory way:
select val,
floor( val / 1000 / 60 / 60 ) as hours,
floor( mod(val / 1000 / 60 , 60) ) as minutes,
floor( mod(val / 1000 , 60) ) as seconds
from (
select 21649000 val from dual union
select 83293000 val from dual
)
VAL HOURS MINUTES SECONDS
---------- ---------- ---------- ----------
21649000 6 0 49
83293000 23 8 13
This does not handle days, so the number of milliseconds must be less than 24 hours.
This gives numbers, you can edit it the way you need to get your desired output format/type.
If you only want second precision you could divide the number by 1000 to get seconds, and by 86400 to get a fraction of a day, then add that to midnight on any nominal date - and convert the result to a string:
select to_char(date '1970-01-01' + (21649000/86400000), 'HH24:MI:SS') as time
from dual;
TIME
--------
06:00:49
select to_char(date '1970-01-01' + (83293000/86400000), 'HH24:MI:SS') as time
from dual;
TIME
--------
23:08:13
This only works properly for values less than a day, i.e. where your original number is less than 86400000; higher than that and you only see the leftover in the second day.
The below query worked for me to convert to the Milliseconds to the HH:MM:SS
SELECT
TO_CHAR(TRUNC([ColumnName]/3600000),'FM9900') || ':' ||
TO_CHAR(TRUNC(MOD([ColumnName],3600000)/60000),'FM00') || ':' ||
TO_CHAR( trunc(MOD([ColumnName],60000)/1000),'FM00') FROM [TableName]`

Calculate Average Time Over 24 hour period

I'm working in Teradata and am trying to calulate the average time a job completes.
Data Values:
Job Name Start Date End Date End Time
D_BDW_CCIP_SRM_LD 10/10/2012 10/11/2012 01:41:49
D_BDW_CCIP_SRM_LD 10/9/2012 10/10/2012 00:19:56
D_BDW_CCIP_SRM_LD 10/8/2012 10/8/2012 23:37:18
D_BDW_CCIP_SRM_LD 10/5/2012 10/5/2012 23:39:47
D_BDW_CCIP_SRM_LD 10/4/2012 10/4/2012 23:42:47
D_BDW_CCIP_SRM_LD 10/3/2012 10/3/2012 23:41:54
The average is coming back with 16:07 instead of 00:07. What I need to happen is that the calculations where the job finishes next day understands that the time expanded.
In Excel I could do this by adding one day to the end time and then averaging and displaying as a time.
How do I do this in Teradata?
This is such an interesting question! UPDATED with correct syntax: Assuming your START_DATE and END_DATE are DATE values and END_TIME is a TIME value, here is a solution:
select cast( avg( case
when start_date <> end_date
then extract(second from end_time)
+ extract(minute from end_time) * 60
+ extract(hour from end_time) * 3600
+ 86400
else extract(second from end_time)
+ extract(minute from end_time) * 60
+ extract(hour from end_time) * 3600
end) mod 86400) as decimal(10,4))
* INTERVAL '00:00:01.00' HOUR TO SECOND as avg_time
from your_table
The CASE expression "adds" one day (86,400 seconds) as you suggested when using Excel to determine the average seconds since midnight into an intermediate result and converted into a TIME column.
To be fair, I received help from the Teradata Forum formatting the result, but I like this so much I'll be using it myself.
This seems to do the trick, but I'd be interested in seeing if there is another way.
SELECT job_name,
case when avg_end_time_in_minutes > 60*24 then avg_end_time_in_minutes - 60*24
else avg_end_time_in_minutes end as avg_adjusted,
case when max_end_time_in_minutes > 60*24 then max_end_time_in_minutes - 60*24
else max_end_time_in_minutes end as max_adjusted,
CAST((CAST(avg_adjusted / 60 AS INTEGER) (FORMAT '9(2)')) AS CHAR(2))||':'||
CAST((CAST((avg_adjusted / 60 MOD 1)*60 AS INTEGER) (FORMAT '9(2)')) AS CHAR(2))
avg_adjusted_time,
CAST((CAST(max_adjusted / 60 AS INTEGER) (FORMAT '9(2)')) AS CHAR(2))||':'||
CAST((CAST((max_adjusted / 60 MOD 1)*60 AS INTEGER) (FORMAT '9(2)')) AS CHAR(2))
max_adjusted_time
FROM (
SELECT job_name,
AVG(end_time_in_minutes) avg_end_time_in_minutes,
MAX(CAST(end_time_in_minutes AS DECIMAL(8,2))) max_end_time_in_minutes
FROM (
SELECT job_name,
CAST(substr(end_time, 1, 2) AS INTEGER)*60
+ CAST(substr(end_time, 4, 2) AS INTEGER)
+ cast(end_date - start_date as integer)*60*24 AS end_time_in_minutes
FROM dabank_prod_ops_tb.bdw_tables_load_tracker_view a
WHERE a.status = 'COMPLETED'
AND a.start_date BETWEEN CURRENT_DATE - 31 AND CURRENT_DATE -1
AND a.end_time IS NOT NULL
) a
GROUP BY 1
) b
First, figure out the number of seconds that the end time is from midnight on the start date. We can then use that to calculate the average number of seconds taken, and then add that to midnight to find the average end time.
select
avg(extract(second from end_time) + 60 *
(extract(minute from end_time) + 60 *
(extract(hour from end_time) + 24 *
(end_date - start_date))) as avg_duration_in_seconds
cast(avg_duration_in_seconds / 60 / 60 as integer) as avg_hours
mod(cast(avg_duration_in_seconds / 60 as integer), 60) as avg_minutes
mod(cast(avg_duration_in_seconds as integer), 60) as avg_seconds,
cast('00:00:00' as time) +
cast(avg_hours as interval hour) +
cast(avg_minutes as interval minute) +
cast(avg_seconds as interval second) as avg_end_time
from my_table
Be aware though that if the average ends up over 24 hours, avg_end_time will be something like 00:01:15 rather than 24:01:15.