SQL- Math with Dates - sql

I am working out of Oracle SQL. I have some dates that may have been poorly formatted when loading.
I'm doing a basic Max(date)-Min(Date) to get the difference in days. My results are:
+000000156 00:00:00.000000
+000000149 00:00:00.000000
+00 00:00:00.000000
I want to do some basic math with these date differences (average, etc) but I get an error message.
How do I convert these strings into numbers?

My guess is that the columns are timestamps, not dates, as the results are intervals not numbers. As you've found, Oracle have not got around to overloading the standard aggregate functions for intervals (vote for this feature on the Oracle Database Ideas forum) and currently you still have to either write your own or cast the timestamps to dates.
with demo (start_date, end_date) as
( select timestamp '2019-12-31 00:00:00', timestamp '2020-06-04 00:00:00' from dual union all
select timestamp '2020-01-31 00:00:00', timestamp '2020-06-28 00:00:00' from dual
)
select end_date - start_date as elapsed_interval
, cast(end_date as date) - cast(start_date as date) as elapsed_days
from demo;
ELAPSED_INTERVAL ELAPSED_DAYS
----------------------------- ------------
+000000156 00:00:00.000000000 156
+000000149 00:00:00.000000000 149

Basic math with dates:
date + number = date + number of days (also fractions)
SELECT SYSDATE + 1 FROM DUAL; -- tomorrow
date - number = date - number of days
SELECT SYSDATE - 1/24 FROM DUAL; -- one hour ago
date - date = numbers of days between dates (also fraction of days)
date + date = impossible
months_between(date1, date2) = returns months between two dates
add_months(date, number) = adds number (months) to date
if you have a string or number and it can be the n-th day of the year (for instance 156),
you can transform in date with TO_DATE('156', 'DDD')
if you have a string with a particular format, you can transform it in date with
TO_DATE(string, format of the date you imagine)
https://www.techonthenet.com/oracle/functions/to_date.php
if you need the opposite transform, that is transforming date to char (or number), use TO_CHAR(date, format of the date)

Related

How to substract 2 varchar dates in oracle?

I have these varchar : 20211026231735.
So I would like a query to substract actual sysdate to that date and convert the substraction to DAY HOURS AND SECONDS.
select TO_CHAR(SYSDATE,'YYYYMMDDHH24MISS') - start_time from TABLEA where job_name='jOB_AA_BB';
I get 4220.
Any help please? Thanks
When you do datetime arithmetic with the DATE datatype, you get back a NUMBER of days. To get an INTERVAL you can subtract two TIMESTAMPs. You don't say what the data type is for start_time, but you might get away with this:
select localtimestamp - start_time
from tablea where job_name='jOB_AA_BB';
LOCALTIMESTAMP gives you a TIMESTAMP value in the current session time zone. There's also CURRENT_TIMESTAMP, which give you the same thing in a TIMESTAMP WITH TIME ZONE and SYSTIMESTAMP that gives you the database time in TIMESTAMP WITH TIME ZONE. You may need to convert your start_time to avoid time zone differences, if any.
You can us the function numtodsinterval to convert the results of date arithmetic to an interval. If necessary then use extract to pull out the needed components.
with tablea(job_name, start_time) as
(select 'jOB_AA_BB','20211026231735' from dual)
select numtodsinterval((SYSDATE - to_date( start_time,'yyyymmddhh24miss')),'hour') date_diff
from tablea where job_name='jOB_AA_BB' ;
with tablea(job_name, start_time) as
(select 'jOB_AA_BB','20211026231735' from dual)
select extract (hour from date_diff) || ':' || extract (minute from date_diff)
from (
select numtodsinterval((sysdate - to_date( start_time,'yyyymmddhh24miss')),'day') date_diff
from tablea where job_name='jOB_AA_BB'
);
NOTE: I am not sure how you got any result, other than an error, as your query winds up as a string - a string. You should not convert sysdate to a string but your string to a date (better yet store it as the proper data type - date).
You can convert the value to a date (rather than converting SYSDATE to a string) and then subtract and explicitly return the value as an INTERVAL DAY TO SECOND type:
SELECT (SYSDATE - TO_DATE('20211026231735', 'YYYYMMDDHH24MISS')) DAY TO SECOND
FROM DUAL;
Or, for your table:
SELECT (SYSDATE - TO_DATE(start_time,'YYYYMMDDHH24MISS')) DAY(5) TO SECOND
FROM TABLEA
WHERE job_name='jOB_AA_BB';
db<>fiddle here

Filtering a dataset by date and time Oracle SQL through Power BI

I'm having trouble with filtering a date and time for anything two hours before and sooner. I tried this:
SELECT *
FROM
table
where
date >= sysdate - 1
AND
TO_DATE( Time, 'HH24:MI:SS' ) >= TO_DATE( sysdate, 'HH24:MI:SS' ) - 2
But I'm getting an inconsistent type error which is what I thought I was handling with the TO_DATE() function but I guess not.
sysdate is already a date (and time), so TO_DATE( sysdate, 'HH24:MI:SS' ) doesn't make any sense.
You didn't provide your data types for your date and time columns in table, so I'm going to assume they're both varchar2(10) with formats MM/DD/YYYY and HH24:MI:SS respectively.
I'm also going to go ahead and change your example table and column names, since they're invalid names to use in a real query.
-- example data
with my_table as (select '06/13/2019' as date_column, '09:40:34' as time_column from dual)
-- your query
SELECT *
FROM
my_table
where
to_date(date_column || ' ' || time_column, 'MM/DD/YYYY HH24:MI:SS') >= sysdate - 2/24
What I'm doing here is to combine your date and time strings into one date-time string, then converting it to an Oracle date type (actually date+time). Then we compare it to sysdate - 2/24, which says to take the current time and subtract 2/24ths of a day, which is 2 hours.
For this example, you might need to change the example data date_column and time_column values to something from the past 2 hours, depending on when you run this and what time zone you're in.

convert int YYYYMMDD to date AS400

I need to convert an integer to a date format in AS400.
I have the field called ivdat8, which is integer in the formatted YYYYMMDD.
I need to use a WHERE clause to select data between two dates, today and 3 days ago.
I am using the below line to do this:
Where
ivdat8 Between (Select current date - 3 days from sysibm.sysdummy1) And (Select current date from sysibm.sysdummy1)
The current date from sysibm is a true date format, but ivdat is integer.
How can i cast ivdat8 to be a date format i can use within the WHERE clause ?
I have tried the below to convert the int to date:
cast(cast(A.ivdat8 as varchar(50)) as date)
&
TIMESTAMP_FORMAT(ivdat8,'YYYYMMDD')
Actually it's better for performance not to convert the ivdat8 column data, but do this with parameters like below.
select ivdat8
from table (values
int(to_char(current date - 2 days, 'YYYYMMDD'))
, int(to_char(current date - 5 days, 'YYYYMMDD'))
) t (ivdat8)
where ivdat8 between
int(to_char(current date - 3 days, 'YYYYMMDD'))
and int(to_char(current date, 'YYYYMMDD'));
Easiest way to do it without causing a complicated conversion in the query is to use this:
cast(digits(cast(A.ivdat8 as dec(8))) || '000000' as date)
The full where clause doesn't need to select from sysibm.dummy1 either.
where cast(digits(cast(A.ivdat8 as dec(8))) || '000000' as date) between current date - 3 days and current date
If you have indexes built on ivdat8 though, the fastest selection will be:
where A.ivdat8 between cast(Current date - 3 days as dec(8)) and cast(Current Date as dec(8))
Managed to convert the int to a date, first i had to cast it as a string, then use TO_DATE
To_Date(cast(A.ivdat8 as varchar(50)),"YYYYMMDD")
try this
Where cast(TIMESTAMP_FORMAT(cast(ivdat8 as varchar(8)), 'YYYYMMDD') as date)
Between (current date - 3 days) and current date

SQL Oracle Unix timestamp conversion

I have two queries which give me different results, can someone explain why this is happening?
The first query uses the unixtime 1533624035000 which represents "07.08.2018 08:40:35" (UTC+2)
select floor((Buchungsdatum - 1533624035000) / (1000*60*60*24)) as Tag,
s.Kurztext as Buchungsstatus,
count(*) as Anzahl
from PfdBuchung b, Schluesselbegriff s
where b.Buchungsdatum >= 1533624035000 and b.SHKennung = 'H' and
b.Buchungsstatus=s.Begriff and s.Oberbegriff='Buchungsstatus' and
b.rzMandant = s.rzMandant
group by floor((Buchungsdatum - 1533624035000) / (1000*60*60*24)), s.Kurztext
order by 1,2`
Result
0 verarbeitet 21800
1 verarbeitet 23380
i have just posted the first two results here
In the 2nd query I convert the unixtimestamp to a datetime with the function POSIX_TO_TIMESTAMP which simple converts my unixts to
l_ora_timestamp := to_timestamp( '1970-01-01 02:00:00', 'yyyy-mm-dd HH24:MI:SS' ) + numtodsinterval( (ptime/1000), 'SECOND' )
select TO_CHAR(POSIX_TO_TIMESTAMP(b.Buchungsdatum), 'YYYY-MM-DD') as Tag,
s.Kurztext as Buchungsstatus, count(*) as Anzahl
from PfdBuchung b, Schluesselbegriff s
where TO_CHAR(POSIX_TO_TIMESTAMP(b.Buchungsdatum), 'YYYY-MM-DD') >= '2018-08-07' and
b.SHKennung = 'H' and
b.Buchungsstatus = s.Begriff and
s.Oberbegriff = 'Buchungsstatus' and
b.rzMandant = s.rzMandant
group by TO_CHAR(POSIX_TO_TIMESTAMP(b.Buchungsdatum), 'YYYY-MM-DD'), s.Kurztext
order by 1,2
Result:
2018-08-07 verarbeitet 15553
2018-08-08 verarbeitet 23315
The reason is pretty obvious.
Both queries count rows where the UNIX timestamp is after 06:40:35 UTC on 7 August 2018.
The first query groups into windows of 24 hours each FROM THIS POINT IN TIME. That is, the first row in the output counts input rows from 06:40:35 on 7 August till same time on 8 August, etc.
The second query counts rows grouped by CALENDAR days (UTC), from midnight to midnight.
There is no reason for the counts to match.
In the second query, you count rows for 7 August, but only input rows with timestamp after 06:40:35 are selected - this is why you only get a count of about 15k, vs. the ~20k for all (full!) 24 hour windows.
And this has nothing to do with time zone. You may be in UTC+2 yourself, but I don't see where in your calculations there is ANY regard paid to timezone.
Your function POSIX_TO_TIMESTAMP will be wrong in winter season due to daylight saving time. Better use
l_ora_timestamp := (timestamp '1970-01-01 00:00:00 UTC' + numtodsinterval(ptime/1000, 'SECOND')) AT TIME ZONE 'Europe/Berlin';

Display correct subtraction of two timestamps in create view

By using normal minus '-' function between two timestamps, the answer given from oracle is incorrect.
This is what i want to do:
ALTER SESSION SET NLS_TIMESTAMP_TZ_FORMAT='DD-MON-RR HH24:MI TZR';
Created table:
CREATE TABLE TEST (
StartTime timestamp with time zone
,EndTime timestamp with time zone
,Science varchar2(7)
);
I create the column data type as timestamp with time zone. This is value I have inserted:
INSERT INTO TEST
VALUES('05-OCT-2013 01:00 +08:00'
,'05-OCT-2013 23:00 +06:00'
,'SCIENCE');
INSERT INTO TEST
VALUES('05-OCT-2013 12:00 +08:00'
,'05-OCT-2013 15:00 -12:00'
,'Maths');
Attempted for rounding time:
CREATE VIEW TESTRECRDS AS
SELECT (Extract(hour FROM(ENDTIME- STARTTIME)) || 'Hours' ||
Extract(minute FROM(ENDTIME- STARTTIME))>=60 Then (Extract(hour FROM(ENDTIME- STARTTIME)) + Extract(minute FROM(ENDTIME- STARTTIME))/60 ELSE 0 END || 'Minutes' AS DURATION,
Science
FROM Test;
Now i have two questions regarding on the calculation and rounding off the minutes to nearest hours.
First let's say the endtime is 1535 +0600 and starttime is 01:50 +0800
So when i deduct endtime - starttime:
the formula should be:
2135 - 0950 = 2085 - 0950
= 1135
But if i use my successful attempt answer to calculate, it is not the correct exact answer. The oracle answer would be 15 hours 45 minutes.
In your last CREATE VIEW statement you try to multiply text, which cannot work:
SELECT To_Char(STARTTIME - ENDTIME, 'HH24:MI TZR')*24 AS DURATION
*24 is operating on the text to_char() returns.
You have to multiply the interval before converting to text.
You define the column Science varchar2(6), then you insert 'SCIENCE', a 7-letter word?
I also fixed a syntax error in your INSERT statement: missing '.
About your comment:
"I would like to insert timestamp with timezone during creation of my tables. Can DATE data type do that too?
Read about data types in the manual.
The data type date does not include time zone information.
If by "timezone difference" you mean the difference between the timezone modifiers, use this to calculate:
SELECT EXTRACT(timezone_hour FROM STARTTIME) AS tz_modifier FROM tbl
Keywords here are timezone_hour and is timezone_minute. Read more in the manual.
But be aware that these numbers depend on the daylight saving hours and such shenanigans. Very uncertain territory!
Get it in pretty format - example:
SELECT to_char((EXTRACT (timezone_hour FROM STARTTIME) * 60
+ EXTRACT (timezone_minutes FROM STARTTIME))
* interval '1 min', 'HH:MI')
In PostgreSQL you would have the simpler EXTRACT (timezone FROM STARTTIME), but I don't think Oracle supports that. Can't test now.
Here is a simple demo how you could round minutes to hours:
SELECT EXTRACT(hour FROM (ENDTIME - STARTTIME))
+ CASE WHEN EXTRACT(minute FROM (ENDTIME - STARTTIME)) >= 30 THEN 1 ELSE 0 END
FROM Test;
I'm not sure what number you're trying to calculate, but when you subtract two dates in Oracle, you get the difference between the dates in units of days, not a DATE datatype
SELECT TO_DATE('2011-01-01 09:00', 'yyyy-mm-dd hh24:mi') -
TO_DATE('2011-01-01 08:00', 'yyyy-mm-dd hh24:mi') AS diff
FROM dual
DIFF
----------
.041666667
In this case 8am and 9am are 0.41667 days apart. This is not a date object, this is a scalar number, so formatting it as HH24:MI doesn't make any sense.
To round you will need to do a bit of more math. Try something like:
TO_DATE(ROUND((ENDTIME - STARTTIME) * 96) / 96, 'HH24:MI')
The difference between dates is in days. Multiplying by 96 changes the measure to quarter hours. Round, then convert back to days, and format. It might be better to use a numeric format want to format, in which case you would divide by 4 instead of 96.
Timezone is not particularly relevant to a time difference. You will have to adjust the difference from UTC to that timezone to get the right result with Timezone included.