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

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]`

Related

Oracle SQL round up time interval to next day

How to round up time interval to next day in Oracle SQL?
select apppackage
, numtodsinterval(
sum( trunc(extract (day from (periods)) * 86400
+ extract (hour from (periods)) *3600
+ extract (minute from (periods))*60
+ extract (second from (periods)))
), 'SECOND') as retention_period
, count(apppackage) as users
from retentions
where apppackage = 'com.Freesoul.Rotter'
group by apppackage;
The output of this is
'com.Freesoul.Rotter' '+2969 04:32:47.000000' '3'
and desired output is
'com.Freesoul.Rotter' '2970' '3'
but if the output of query is
'com.Freesoul.Rotter' '+2969 00:00:00.000000' '3'
then desired output is
'com.Freesoul.Rotter' '2969' '3'
column period is of INTERVAL DAY(9) TO SECOND(6) type
and i won't mind if the retention_period is changed to number datatype.
I'll be grateful if anyone can suggest change in my query to attain the desired output.
The result of your sum is in seconds, so you don't really need to convert it to an interval at all. Just divide by 60*60*24 to get the answer in days, and round it up with ceil():
select apppackage
, ceil(sum( trunc(extract (day from (periods)) * 86400
+ extract (hour from (periods)) *3600
+ extract (minute from (periods))*60
+ extract (second from (periods)))
) / 86400) as retention_period
, count(apppackage) as users
from retentions
where apppackage = 'com.Freesoul.Rotter'
group by apppackage;
Demo with artificial data in a CTE just to mimic your expected results, for both scenarios:
-- CTE for sample data
with retentions (apppackage, periods) as (
select 'com.Freesoul.Rotter', interval '+2967 04:32:47.000000' day(9) to second(6) from dual
union all
select 'com.Freesoul.Rotter', interval '1' day from dual
union all
select 'com.Freesoul.Rotter', interval '1' day from dual
union all
select 'com.Freesoul.XYZ', interval '+2967 00:00:00.000000' day(9) to second(6) from dual
union all
select 'com.Freesoul.XYZ', interval '1' day from dual
union all
select 'com.Freesoul.XYZ', interval '1' day from dual
)
-- actual query
select apppackage
, ceil(sum( trunc(extract (day from (periods)) * 86400
+ extract (hour from (periods)) *3600
+ extract (minute from (periods))*60
+ extract (second from (periods)))
) / 86400) as retention_period
, count(apppackage) as users
from retentions
where apppackage = 'com.Freesoul.Rotter'
-- extra clause for dummy data
or apppackage = 'com.Freesoul.XYZ'
group by apppackage;
APPPACKAGE RETENTION_PERIOD USERS
------------------- ---------------- ----------
com.Freesoul.XYZ 2969 3
com.Freesoul.Rotter 2970 3
Your expected output shows a plain number. If you actually want it as an interval, but as the whole number of days, just pass ceil'd number into numtodsinterval or more simply (and usually faster for some reason) multiply by interval '1' day.
With the same dummy data:
select apppackage
, ceil(sum( trunc(extract (day from (periods)) * 86400
+ extract (hour from (periods)) *3600
+ extract (minute from (periods))*60
+ extract (second from (periods)))
) / 86400) * interval '1' day as retention_period
, count(apppackage) as users
...
APPPACKAGE RETENTION_PERIOD USERS
------------------- --------------------- ----------
com.Freesoul.XYZ +2969 00:00:00.000000 3
com.Freesoul.Rotter +2970 00:00:00.000000 3
As #mathguy pointed out, you probably don't need or want the trunc() call in there; that is removing the fractional seconds from each period before they are summed, which sounds insignificant but could easily affect the result you get.
How about this?
CASE WHEN numtodsinterval(extract (day from periods), 'DAY') = periods THEN
extract (day from periods)
ELSE
extract (day from periods) + 1
END

LAG Function in Oracle

I have a table (incident) that has column Create_date(DataType=Date).
I want to get difference in Days OR Hours from Previous Record. Like the screenshot below.
From Second Record Create_Date I want to minus First Create_Date and from Third Create Date to Second and so on. I'm using LAG function in Oracle, but not sure how its calculating there. Could any one please help me regarding that issue.
incident.create_date - lag(incident.create_date,1) OVER (ORDER BY incident.create_date) AS CREATEDATE_DIFF,
RN 1 We have Create_date (05/01/017 10:40:17 AM
Date differences in Oracle are calculated in numbers of days. If the difference is less than a day, you're going to get a value of less than 1 returned.
If you want to convert that into hours, you'll have to multiply the result by 24, for minutes multiply by 24*60 and for seconds it's 24*60*60.
e.g.:
select sysdate - trunc(sysdate) diff_in_days,
(sysdate - trunc(sysdate))*24 diff_in_hours,
(sysdate - trunc(sysdate))*24*60 diff_in_mins,
(sysdate - trunc(sysdate))*24*60*60 diff_in_secs
from dual;
DIFF_IN_DAYS DIFF_IN_HOURS DIFF_IN_MINS DIFF_IN_SECS
------------ ------------- ------------ ------------
0.4342245370 10.4213888888 625.28333333 37517
You may then wish to apply ROUND (or maybe TRUNC/CEIL) depending on how you want the output to look like (e.g. to 2 d.p., to nearest minute, etc).
If you subtract one date from another you will get the difference in days (or fractions thereof) as a number.
You can get the days/hours/minutes/seconds of this using an interval:
SELECT EXTRACT( DAY FROM createdate_diff ) AS days,
EXTRACT( HOUR FROM createdate_diff ) AS hours,
EXTRACT( MINUTE FROM createdate_diff ) AS minutes,
EXTRACT( SECOND FROM createdate_diff ) AS seconds,
createdate_diff
FROM (
SELECT NUMTODSINTERVAL(
create_date - lag(create_date) OVER (ORDER BY create_date),
'DAY'
) AS CREATEDATE_DIFF
FROM incident
);
Or you can perform the same calculations manually:
SELECT TRUNC( createdate_diff ) AS days,
TRUNC( MOD( createdate_diff * 24, 24 ) ) AS hours,
TRUNC( MOD( createdate_diff * 24 * 60, 60 ) ) AS minutes,
MOD( createdate_diff * 24 * 60 * 60, 60 ) AS seconds,
createdate_diff
FROM (
SELECT create_date - lag(create_date) OVER (ORDER BY create_date)
AS CREATEDATE_DIFF
FROM incident
);
Use
......
(incident.create_date -
lag(incident.create_date,1) OVER (ORDER BY incident.create_date))
*24*60
AS CREATEDATE_DIFF_IN_MINS,.....
to get the output in minutes, which seams suitable for your sample data. Or multiply further by 60 to get output in seconds.

How to format interval to hours : minutes in pgsql

When we use this query
select '2930:41:51.974223'::interval(0);
Output
interval
------------
2930:41:52
Desired Output
2930:42
I tried
select to_char('2930:41:51.974223'::interval(0),'HH24:MI');
Results:
to_char
---------
2930:41
It won't round 41 to 42 by adding the seconds)
Simply add 30 seconds to the interval:
select to_char('2930:41:51.974223'::interval(0)+ '30s','HH24:MI');
to_char
---------
2930:42
(1 row)
select to_char('2930:41:51.974223' + interval '30','HH24:MI');
or
select to_char('2930:41:51.974223' + interval '30 seconds','HH24:MI');

Averaging a list of TIMESTAMP(6) WITH TIME ZONE times

I've got 2 columns in a database of type TIMESTAMP(6) WITH TIME ZONE. I've subtracted one from the other to get the time between the two timestamps.
select lastprocesseddate-importeddate
from feedqueueitems
where eventid = 2213283
order by written desc;
How can I get an average of the list of time differences I have?
Here are a small sample of time differences:
+00 00:00:00.488871
+00 00:00:00.464286
+00 00:00:00.477107
+00 00:00:00.507042
+00 00:00:00.369144
+00 00:00:00.488918
+00 00:00:00.354797
+00 00:00:00.378801
+00 00:00:00.320040
+00 00:00:00.361242
+00 00:00:00.302327
+00 00:00:00.331441
+00 00:00:00.324065
EDIT: I also should have noted - I've tried the AVG function, and it just returns
ORA-00932: inconsistent datatypes: expected NUMBER got INTERVAL DAY TO SECOND
00932. 00000 - "inconsistent datatypes: expected %s got %s"
*Cause:
*Action:
Error at Line: 3 Column: 29
EDIT2: Just to clarify the above snippet. Line 3 is my SQL query all on one line in the following format:
select AVG(lastprocesseddate-importeddate) from feedqueueitems where eventid = 2213283;
EDIT3: Massive thanks to Matt and Alex Poole. You've both helped massively and I appreciate you taking the time to help with this and to both consistently return with updated help in response to the feedback/further problems! Thanks guys!
Use the AVG function
SELECT avg(cast(lastprocesseddate as date)-cast(importeddate as date))
FROM feedqueueitems
WHERE eventid = 2213283
ORDER BY written DESC;
On the Database with the +1 timezone for importeddate and lastprocesseddate is UTC
SELECT avg(cast(cast(lastprocesseddate as timestamp with time zone) at time zone '+01:00' as date)-cast(importeddate as date))
FROM feedqueueitems
WHERE eventid = 2213283
ORDER BY written DESC;
You could extract the time components from each gap value, which is an interval data type, so you end up with a figure in seconds (including the fractional part), and then average those:
select avg(extract(second from gap)
+ extract(minute from gap) * 60
+ extract(hour from gap) * 60 * 60
+ extract(day from gap) * 60 * 60 * 24) as avg_gap
from (
select lastprocesseddate-importeddate as gap
from feedqueueitems
where eventid = 2213283
);
A demo using a CTE to provide the interval values you showed:
with cte as (
select interval '+00 00:00:00.488871' day to second as gap from dual
union all select interval '+00 00:00:00.464286' day to second from dual
union all select interval '+00 00:00:00.477107' day to second from dual
union all select interval '+00 00:00:00.507042' day to second from dual
union all select interval '+00 00:00:00.369144' day to second from dual
union all select interval '+00 00:00:00.488918' day to second from dual
union all select interval '+00 00:00:00.354797' day to second from dual
union all select interval '+00 00:00:00.378801' day to second from dual
union all select interval '+00 00:00:00.320040' day to second from dual
union all select interval '+00 00:00:00.361242' day to second from dual
union all select interval '+00 00:00:00.302327' day to second from dual
union all select interval '+00 00:00:00.331441' day to second from dual
union all select interval '+00 00:00:00.324065' day to second from dual
)
select avg(extract(second from gap)
+ extract(minute from gap) * 60
+ extract(hour from gap) * 60 * 60
+ extract(day from gap) * 60 * 60 * 24) as avg_gap
from cte;
AVG_GAP
----------
.397544692
Or if you wanted it as an interval:
select numtodsinterval(avg(extract(second from gap)
+ extract(minute from gap) * 60
+ extract(hour from gap) * 60 * 60
+ extract(day from gap) * 60 * 60 * 24), 'SECOND') as avg_gap
...
which gives
AVG_GAP
--------------------
0 0:0:0.397544692
SQL Fiddle with answer in seconds. (It doesn't seem to like displaying intervals at the moment, so can't demo that).
This query should solve the issue.
WITH t AS
(SELECT
TIMESTAMP '2015-04-23 12:00:00.5 +02:00' AS lastprocesseddate,
TIMESTAMP '2015-04-23 12:05:10.21 UTC' AS importeddate
FROM dual)
SELECT
AVG(
EXTRACT(SECOND FROM SYS_EXTRACT_UTC(lastprocesseddate) - SYS_EXTRACT_UTC(importeddate))
+ EXTRACT(MINUTE FROM SYS_EXTRACT_UTC(lastprocesseddate) - SYS_EXTRACT_UTC(importeddate)) * 60
+ EXTRACT(HOUR FROM SYS_EXTRACT_UTC(lastprocesseddate) - SYS_EXTRACT_UTC(importeddate)) * 60 * 60
+ EXTRACT(DAY FROM SYS_EXTRACT_UTC(lastprocesseddate) - SYS_EXTRACT_UTC(importeddate)) * 60 * 60 * 24
) AS average_gap
FROM t;

Converting time difference to a given format in Oracle

How do I convert EVENT_DATE_B - EVENT_DATE_A which is a number of days to string with HH:MM format?
Another approach (one query can be on different days):
with tt as (
select numToDsinterval((EVENT_DATE_B - EVENT_DATE_A ), 'DAY') dsint
from t)
select (extract(day from dsint)*24)+extract(hour from dsint) ||
':' ||extract(minute from dsint)
from tt
Here is a sqlfiddle demo
If dates are differ only in time part you can use interval day to second. For instance:
SQL> select (to_date('25.12.12 15:37:32', 'DD.MM.YY HH24:MI:SS')
2 - to_date('25.12.12 12:45:45', 'DD.MM.YY HH24:MI:SS')) day(0) to second(0) as Time
3 from dual
4 ;
TIME
-------------
+0 02:51:47
But obviously it will not always be the case. So you could write a long query to calculate different parts of time, but I think I would go with this simple function:
SQL> create or replace function DaysToTime(p_val in number)
2 return varchar2
3 is
4 l_hours number;
5 l_minutes number;
6 l_seconds number;
7 begin
8 l_Hours := 24 * p_val;
9 l_minutes := (l_hours - trunc(l_hours)) * 60;
10 l_seconds := (l_minutes - trunc(l_minutes)) * 60;
11 return to_char(trunc(l_hours), 'fm09') ||':'||
12 to_char(trunc(l_minutes), 'fm09')||':'||
13 to_char(trunc(l_seconds), 'fm09');
14 end;
15 /
Function created
And now the query would be:
SQL> select DaysToTime(to_date('25.12.12 15:37:32', 'DD.MM.YY HH24:MI:SS')
2 - to_date('25.12.12 12:45:45', 'DD.MM.YY HH24:MI:SS')) as Time
3 from dual
4 ;
TIME
----------
02:51:47
select 24 * (EVENT_DATE_B - EVENT_DATE_A) || ':' || '00'
from your_table
SQLFiddle demo
I think you need to find out the number of days between date1 and date2, then subtract this difference from date2 and convert final date to your format. Copy/paste and see the output:
Select date2, days_between, to_char(date2-days_between, 'mm-dd-yyyy hh24:mi:ss') end_date
From
(
Select sysdate date2
, trunc(sysdate)-to_date('20-DEC-2012') days_between --'20-DEC' is start_date
From dual
)
/