How to calculate the difference of HH:MM:SS between two dates in oracle sql? - oracle

I have a table abc as:
-- start_time |end_time | total_time_taken
-- 27.05.2020 00:52:48 |27.05.2020 02:08:33 |
I want to set the value of total_time_taken as the difference of end_time-start_time. in the format "HH:MM:SS".I searched the similar topic but didnot find the exact answer.
My expected output is like : 01:44:12 (HH:MM:SS)
So,i tried :
SELECT To_Char(end_time,'HH24:MM:SS'),To_Char(start_time,'HH24:MM:SS'),
To_Char(end_time,'HH24:MM:SS')-To_Char(start_time,'HH24:MM:SS') FROM abc;
The datatypes of start_time,end_time,total_time_taken is DATE.Please help me to find the solution.

If you cast those dates as timestamps, you can easily subtract them and see relatively nice result:
SQL> with test (st, et) as
2 (select to_date('27.05.2020 00:52:48', 'dd.mm.yyyy hh24:mi:ss'),
3 to_date('27.05.2020 02:08:33', 'dd.mm.yyyy hh24:mi:ss')
4 from dual
5 )
6 select cast(et as timestamp) - cast(st as timestamp) diff
7 from test;
DIFF
--------------------------------------------------------------------------
+000000000 01:15:45.000000
SQL>
If you want to format it as you wanted (note that mm format mask is for months; mi is for minutes), then you could do some extracting - again from timestamp (won't work for date):
SQL> with test (st, et) as
2 (select to_date('27.05.2020 00:52:48', 'dd.mm.yyyy hh24:mi:ss'),
3 to_date('27.05.2020 02:08:33', 'dd.mm.yyyy hh24:mi:ss')
4 from dual
5 ),
6 diff as
7 (select cast(et as timestamp) - cast(st as timestamp) diff
8 from test
9 )
10 select extract(hour from diff) ||':'||
11 extract(minute from diff) ||':'||
12 extract(second from diff) diff
13 from diff;
DIFF
-------------------------------------------------------------------------
1:15:45
SQL>
You can further make it pretty (e.g. two digits for hours, using LPAD function). Or, you can even write your own function which will actually work on difference of DATE datatype values, do some calculations (using trunc function, subtractions, whatnot), but the above looks pretty elegant if compared to a home-made function.

The answer by Littlefoot is perfectly fine. This answer is just to show there is more than one way to get the result.
First, we can subtract one date from another and get the difference in days, then convert that difference to an interval.
with test (st, et) as
(select to_date('27.05.2020 00:52:48', 'dd.mm.yyyy hh24:mi:ss'),
to_date('27.05.2020 02:08:33', 'dd.mm.yyyy hh24:mi:ss')
from dual
)
select numtodsinterval(et-st, 'day') diff
from test;
Then, since we can't control interval formatting directly, we can add DIFF to an arbitrary date and then use built-in date formatting.
with test (st, et) as
(select to_date('27.05.2020 00:52:48', 'dd.mm.yyyy hh24:mi:ss'),
to_date('27.05.2020 02:08:33', 'dd.mm.yyyy hh24:mi:ss')
from dual
)
select to_char(date '1-1-1' + numtodsinterval(et-st, 'day'), 'hh24:mi:ss') diff
from test;
DIFF
--------
01:15:45

Related

Use EPOCH time for timestamp to get records within 1 minute

I was curious to see how in Oracle 12c you can take a timestamp datatype and convert the records into EPOCH time to make them a number and then use that number to find any records within that date column that are within 1 minute of each other (assuming the same day if needed, or simply any calculations within 1 minute).
I tried the following but got an ORA-01873: the leading precision of the interval is too small error.
select (sold_date - to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS'))*86400 as epoch_sold_date from test1;
What is SOLD_DATE? For e.g. SYSDATE (function that returns DATE datatype), your code works OK.
SQL> select (sysdate
2 - to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS')
3 ) * 86400 as epoch_sold_date
4 from dual;
EPOCH_SOLD_DATE
---------------
1600807918
SQL>
As SOLD_DATE is a timestamp, but - it appears that fractions of a second aren't or special interest to you, cast it to DATE:
select (cast (systimestamp as date) --> this
- to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS')
) * 86400 as epoch_sold_date
from dual;
Saying that you get the same result for all rows: well, I don't, and you shouldn't either if SOLD_DATE differs.
SQL> with test (sold_date) as
2 (select timestamp '2020-09-22 00:00:00.000000' from dual union all
3 select timestamp '2015-03-18 00:00:00.000000' from dual
4 )
5 select sold_date,
6 (cast (sold_date as date)
7 - to_date('1970-01-01 00:00:00','YYYY-MM-DD HH24:MI:SS')
8 ) * 86400 as epoch_sold_date
9 from test;
SOLD_DATE EPOCH_SOLD_DATE
------------------------------ ---------------
22.09.20 00:00:00,000000000 1600732800
18.03.15 00:00:00,000000000 1426636800
SQL>
One more edit: when you subtract two timestamps, result is interval day to second. If you extract minutes from it, you get what you wanted:
SQL> with test (sold_date) as
2 (select timestamp '2020-09-22 10:15:00.000000' from dual union all
3 select timestamp '2015-03-18 08:05:00.000000' from dual
4 )
5 select sold_date,
6 lead(sold_date) over (order by sold_date) next_sold_date,
7 --
8 lead(sold_date) over (order by sold_date) - sold_date diff,
9 --
10 extract(day from lead(sold_date) over (order by sold_date) - sold_date) diff_mins
11 from test
12 order by sold_date;
SOLD_DATE NEXT_SOLD_DATE DIFF DIFF_MINS
------------------------------ ------------------------------ ------------------------------ ----------
18.03.15 08:05:00,000000000 22.09.20 10:15:00,000000000 +000002015 02:10:00.000000000 2015
22.09.20 10:15:00,000000000
SQL>
In your case, you'd check whether extracted minutes value is larger than 1 (minute).
If you just want to see how many minutes are there between two timestamps, then
cast them to dates
subtract those dates (and you'll get number of days)
multiply it by 24 (as there are 24 hours in a day) and by 60 (as there are 60 minutes in an hour)
Something like this:
SQL> with test (date_1, date_2) as
2 (select timestamp '2020-09-22 10:15:00.000000',
3 timestamp '2020-09-22 08:05:00.000000' from dual
4 )
5 select (cast(date_1 as date) - cast(date_2 as date)) * 24 * 60 diff_minutes
6 from test;
DIFF_MINUTES
------------
130
SQL>
If you are just looking to compare dates and find rows that are within one minute of each other, you do not need to use epoch time. There are several solutions to this problem on this thread.

Oracle sql how to get start and end dates of weeks between two dates

I want to get the start and end days of every week between two dates. The dates's format is dd/mm/yyy hh24:mi:ss. I need the weeks in the format dd/mm/yyyy hh24:mi:ss because I have to calculate the days and hours between the start and end day of the week with the times
I wrote this statement
WITH
date_range AS (
SELECT
pdm.des_comercial serie,
pdm.id_material codserie,
ri.id_accion intervencion,
TO_CHAR(NVL(ri.fecha_salida_rev, SYSDATE), 'dd/mm/RRRR') fecha1,
to_char((CASE
WHEN ri.fecha_salida_rev > TO_DATE('18/06/2019', 'dd/mm/yyyy') THEN TO_DATE('18/06/2019', 'dd/mm/yyyy')
WHEN ri.fecha_salida_Rev IS NULL THEN TO_DATE('18/06/2019', 'dd/mm/yyyy')
ELSE ri.fecha_salida_Rev
END),'dd/mm/yyyy hh24:mi:ss') fechasalida,
to_char((CASE
WHEN ri.fecha_entrada_rev < TO_DATE('01/06/2019', 'dd/mm/yyyy') THEN TO_DATE('01/06/2019', 'dd/mm/yyyy')
ELSE ri.fecha_entrada_Rev
END),'dd/mm/yyyy hh24:mi:ss') fechaentrada
,
ri.cod_taller_rev,
ri.COD_MATRICULA,
ri.fecha_entrada_rev start_date,
ri.fecha_salida_rev end_date
FROM
r_intervencion ri,
planificador.pl_dh_material pdm
WHERE
ri.id_accion = ri.amortizada_por
AND ri.causa_entrada = 1
AND ri.tipo_accion = 1
AND pdm.id_material = ri.cod_serie
AND pdm.hasta = 99999999
AND ri.ID_ACCION = 'IM4'
AND ri.fecha_salida_rev BETWEEN TO_DATE('01/06/2019', 'dd/mm/yyyy') AND TO_DATE('18/06/2019', 'dd/mm/yyyy')
),
semanas AS (
SELECT LEVEL "Week"
,to_char(to_date(start_date,'dd/mm/yyyy hh24:mi:ss') + (7 * (LEVEL - 1)),'IW') startweek
,to_char(to_date(start_date ,'dd/mm/yyyy hh24:mi:ss')+ (7 * (LEVEL - 1)),'IW') + 6 endweek
,TO_CHAR(start_date + (7 * (LEVEL - 1)),'IW') "Iso Week",
serie,
codserie,
intervencion,
cod_taller_rev,
cod_matricula,
fechaentrada,
fechasalida,
start_date,
end_date
FROM date_range
CONNECT BY LEVEL <= (to_char(To_date(end_date,'dd/mm/yyyy hh24:mi:ss'),'IW') - to_char(To_date(start_date,'dd/mm/yyyy hh24:mi:ss'),'IW')) / 7 + 1
)
SELECT startweek,
endweek,
to_date(endweek,'dd/mm/yyyy hh24:mi:ss') - to_date(startweek,'dd/mm/yyyy hh24:mi:ss') dias,
serie,
codserie,
intervencion,
cod_taller_rev,
cod_matricula,
start_Date,
end_date,
fechaentrada,
fechasalida,
rd.descripcion
FROM semanas,r_depositos rd
WHERE cod_taller_rev = rd.cod_deposito
When I execute it, I get
Query execution failed
SQL Error [1840] [22008]: ORA-01840: ORA-01840: input value not long enough for date format
The error is in
,to_char(to_date(start_date,'dd/mm/yyyy hh24:mi:ss') + (7 * (LEVEL - 1)),'IW') startweek
,to_char(to_date(start_date ,'dd/mm/yyyy hh24:mi:ss')+ (7 * (LEVEL - 1)),'IW') + 6 endweek
How can I get the startweek and endweek with the format dd/mm/yyyy hh24:mi:ss
EDITED
start_date end_date
20/05/2019 20:00:00 05/06/2019 08:00:00
weeks
20/05/2019 20:00:00 26/05/2019 -> 6 days and xxx hours
27/05/2019 02/06/2019 -> 7 days
03/06/2019 05/06/2019 08:00:00 -> 3 days and xxx hours
I need to calculate the difference in days and hours for each week.
For example between 20/05/2019 20:00:00 and 26/05/2019
and last one between 03/06/2019 and 05/06/2019 08:00:00
My issue is with the calculation
to_date(endweek,'dd/mm/yyyy hh24:mi:ss') - to_date(startweek,'dd/mm/yyyy hh24:mi:ss') dias,
endweek and startweek have to have dd/mm/yyyy hh24:mi:ss
My issue is with the calculation
to_date(endweek,'dd/mm/yyyy hh24:mi:ss') - to_date(startweek,'dd/mm/yyyy hh24:mi:ss') dias,
endweek and startweek have to have dd/mm/yyyy hh24:mi:ss
Oracle dates are stored in an internal format which you generally don't need to worry about. Your application or client formats the date as a string, based on its own setting or your session NLS settings.
When you do something like:
to_date(endweek,'dd/mm/yyyy hh24:mi:ss')
you're really doing:
to_date(to_char(endweek),'dd/mm/yyyy hh24:mi:ss')
and as there is no explicit format mask specified for the implicit to_char() call it used your current session's NLS settings. Depending on the setting it might error; or might corrupt the value - e.g. mixing YY and YYYY masks can lose the century, converting 2019 to 0019. (Given the error you are getting, your NLS settings might be unusual?).
At best you're converting the date value to a string and back to exactly the same date value, which is pointless. You aren't changing the format of the datem because it doesn't have one. The intermediate string does, but you aren't using that, and you can't for calculations (at least without converting back to a date as you are, which again is pointless.)
Oracle has other functions to manipulate date values, including trunc(), so I think you might want something like this - showing the difference in three ways, though there are others and you can format the last one however you want:
with date_range (start_date, end_date) as (
-- dummy data from your example
select to_date('20/05/2019 20:00:00', 'DD/MM/YYYY HH24;MI:SS') as start_date,
to_date('05/06/2019 08:00:00', 'DD/MM/YYYY HH24;MI:SS') as end_date
from dual
),
semanas as (
select level as week,
start_date,
end_date,
greatest(trunc(start_date + (7 * (level - 1)), 'IW'), start_date) as start_week,
least(trunc(start_date + (7 * level), 'IW'), end_date) as end_week
from date_range
connect by level <= (trunc(end_date, 'IW') - trunc(start_date, 'IW')) / 7 + 1
)
select week,
to_char(start_week, 'IW') as iso_week,
to_char(start_week, 'DD/MM/YYYY HH24:MI:SS') as start_week,
to_char(end_week, 'DD/MM/YYYY HH24:MI:SS') as end_week,
end_week - start_week as diff_num,
numtodsinterval(end_week - start_week, 'DAY') as diff_interval,
to_char(date '1999-12-31' + (end_week - start_week), 'FMDD "days" HH24 "hours"') as diff_words
from semanas;
WEEK IS START_WEEK END_WEEK DIFF_NUM DIFF_INTERVAL DIFF_WORDS
---------- -- ------------------- ------------------- ---------- ------------------- ----------------
1 21 20/05/2019 20:00:00 27/05/2019 00:00:00 6.16666667 +06 04:00:00.000000 6 days 4 hours
2 22 27/05/2019 00:00:00 03/06/2019 00:00:00 7 +07 00:00:00.000000 7 days 0 hours
3 23 03/06/2019 00:00:00 05/06/2019 08:00:00 2.33333333 +02 08:00:00.000000 2 days 8 hours
As currently written the connect by only works properly if the date_range CTE generates a single value; if you actually get multiple rows back from your real query then you'll have to do a bit more work, or switch to recursive CTEs, or cross join/apply, depending on your Oracle version.
Your endweek calculation is
to_char(to_date(start_date ,'dd/mm/yyyy hh24:mi:ss')+ (7 * (LEVEL - 1)),'IW') + 6 endweek
This attempts to add the number 6 to a character string. I suspect that what you wanted was
to_char(to_date(start_date ,'dd/mm/yyyy hh24:mi:ss') + (7 * (LEVEL - 1) + 6),'IW') endweek
Here I've moved the + 6 so you're adding 6 to the date value, rather than to a character string.

Selecting every 3 seconds data rows between 2 dates

I have a requirement to take every 3 seconds data within the specific time interval in SQL. I am new to SQL so can anyone help me on the scenario
This is my select query which returns all the values but i need data for every 3 seconds only
SELECT ton_nbr
FROM
icr_file_interface
WHERE
(
reading_dttm BETWEEN
TO_DATE(concat('2016-10-19',to_char(0930)),'yyyy-mm-dd HH24MISS')
AND TO_DATE(concat('2016-10-19',to_char('0945')),'yyyy-mm-dd HH24MISS')
)
AND
(
ton_nbr BETWEEN
(SELECT value FROM text_para WHERE para_cd='ICR_ST_RNG')
AND (SELECT value FROM text_para WHERE para_cd='ICR_ED_RNG')
)
If you only need to subtract 3 seconds from a date, you can use the following:
SQL> select to_char(sysdate, 'yyyy-mm-dd hh24:mi:ss'),
2 to_char(sysdate - 3*1/24/60/60, 'yyyy-mm-dd hh24:mi:ss')
3 from dual;
TO_CHAR(SYSDATE,'YY TO_CHAR(SYSDATE-3*1
------------------- -------------------
2016-10-19 09:38:17 2016-10-19 09:38:14
Given that sysdate -1 means "subtract one day to sysdate", you can derive the number of seconds you need with a bit af arithmetic
This selects you data between last 3 seconds. Hope you got the idea.
select ton_nbr
from icr_file_interface
where reading_dttm between dateadd(ss, -3, getdate()) and getdate() ;

concatenating sysdate with fixed time in oracle

I want to concatenate sysdate with my own time. Below is the query i have tried, but I am getting year as 0016 instead of 2016.
Are there any other ways to get the result like below?
Query:
select to_date(sysdate || ' 02:50:00','dd-mon-yyyy hh24:mi:ss') as MyTime from dual
Output:
MYTIME
3/12/0016 02:50:00 AM
One way
Convert SYSDATE to a string
Append your fixed time element
Convert back to a date
Put it altogether like this:
to_date(to_char(sysdate,'YYYY-MM-DD')||' 02:50:00','YYYY-MM-DD HH24:MI:SS' )
Alternative way: use arithmetic.
Strip the real time from SYSDATE
Add the number of seconds ((60*60*2)+(50*60)/(60*60*24)
Include your workings or not:
trunc(sysdate) + ( 10200 / 86400)
There is a third way: use an INTERVAL (basically a variant of the second way).
SQL> select trunc(sysdate) + to_dsinterval('0 02:50:00') as mytime
2 from dual
3 /
MYTIME
------
2016-03-12 02:50:00
SQL>
Try this
SELECT TO_DATE(TO_CHAR(SYSDATE, 'DD-MON-YYYY') || ' 08:00:00', 'DD-MON-YYYY HH:MI:SS') FROM DUAL;
Just change SYSDATE to TO_CHAR(SYSDATE, 'DD-MON-YYYY')
I did this:
SELECT TO_CHAR(
TO_DATE(TO_CHAR(SYSDATE, 'DD-MON-YYYY') || ' 08:00:00', 'DD-MON-YYYY HH:MI:SS')
,'DD-MON-YYYY HH:MI:SS') from dual;
and got
12-MAR-2016 08:00:00
as a result.

Using a case when to know if date format is right

I want to migrate a table which contains some columns with dates. The issue is my dates are often in dd/mm/yyyyy HH24:MM:YYYY format. But sometimes it appears that the format is only dd/mm/yyyy, or blank.
I guess that's why I'm getting ORA-01830 when I'm trying to migrate the datas.
I tried
CASE
WHEN TO_DATE(MYDATE,'DD/MM/YYYY')
then TO_DATE(MYDATE,'DD/MM/YYYY 00:00:00')
END AS MYDATE
But I'm not sure if it is possible to test the date format (and ofcourse it's not working).
Thank you
TO_DATE cannot test date format, but you can do it. If Lalit's answer would not be enough, try something like
select
case when my_date like '__/__/__' then to_date(my_date, 'dd/mm/yy')
when my_date like '__-__-__' then to_date(my_date, 'dd-mm-yy')
...
end
So you have the data type issue. DATE is stored as string literal. As you have mentioned that the date model has the DD/MM/YYYY part same, just that the time portion is either missing for some rows or the entire value is NULL.
For example, let's say your table have the values like -
SQL> WITH dates AS(
2 SELECT 1 num, '29/12/2014 16:38:57' dt FROM dual UNION ALL
3 SELECT 2, '29/12/2014' FROM dual UNION ALL
4 SELECT 3, NULL FROM dual
5 )
6 SELECT num, dt
7 FROM dates
8 /
NUM DT
---------- -------------------
1 29/12/2014 16:38:57
2 29/12/2014
3
SQL>
TO_DATE with proper format model should do the trick.
Let's stick to a format model first.
SQL> alter session set nls_date_format='dd/mm/yyyy hh24:mi:ss';
Session altered.
Now, let's use TO_DATE to explicitly convert the string literal to date.
SQL> WITH dates AS(
2 SELECT 1 num, '29/12/2014 16:38:57' dt FROM dual UNION ALL
3 SELECT 2, '29/12/2014' FROM dual UNION ALL
4 SELECT 3, NULL FROM dual
5 )
6 SELECT num, to_date(dt, 'dd/mm/yyyy hh24:mi:ss') dt
7 FROM dates
8 /
NUM DT
---------- -------------------
1 29/12/2014 16:38:57
2 29/12/2014 00:00:00
3
SQL>

Resources