Reputation: 101
I have a very basic question reg date calculations in Oracle SQL.
I have been using the following logic to calculate the age and identify records with age>=75:
(Date1 - Date2) / 365.25 >= 75
However the above logic was calculating the age incorrectly for the following scenario:
Date1 = MM-DD-2016
Date2 = MM-DD-1947
Although, here the age is 69, it was showing up in my output. After I added to_date for both the dates, the query did not bring this record.
My question is: I have not used to_date earlier in this query and was working for the other records fine, but why has it failed for this scenario?
Edit: I wanted to add, looks like my database records values as below when partial dates are entered: Date1 is 6/15/1947 Date2 is 6/15/2016
Upvotes: 0
Views: 1931
Reputation: 191275
The only way i can see you'd get that result is if you have a date stored as -1947 instead of 1947:
with t (Date1, Date2) as (
select date '2016-06-15', add_months(date '2016-06-15', -12*level)
from dual
connect by level <= 80
union all
select date '2016-06-15', date '-1947-06-15'
from dual
)
select * from t
where (Date1 - Date2) / 365.25 >= 75;
DATE1 DATE2
---------- ----------
06/15/2016 06/15/1941
06/15/2016 06/15/1940
06/15/2016 06/15/1939
06/15/2016 06/15/1938
06/15/2016 06/15/1937
06/15/2016 06/15/1936
06/15/2016 06/15/1947
That last record looks like it shouldn't be there. But my NLS_DATE_FORMAT mask is MM/DD/YYYY (to match what you're seeing); if I make it show the sign of the year it makes more sense:
alter session set nls_date_format = 'MM/DD/SYYYY';
with t (Date1, Date2) as (
select date '2016-06-15', add_months(date '2016-06-15', -12*level)
from dual
connect by level <= 80
union all
select date '2016-06-15', date '-1947-06-15'
from dual
)
select * from t
where (Date1 - Date2) / 365.25 >= 75;
DATE1 DATE2
------------- -------------
6/15/ 2016 6/15/ 1941
6/15/ 2016 6/15/ 1940
6/15/ 2016 6/15/ 1939
6/15/ 2016 6/15/ 1938
6/15/ 2016 6/15/ 1937
6/15/ 2016 6/15/ 1936
6/15/ 2016 6/15/-1947
Or even better:
with t (Date1, Date2) as (
select date '2016-06-15', add_months(date '2016-06-15', -12*level)
from dual
connect by level <= 80
union all
select date '2016-06-15', date '-1947-06-15'
from dual
)
select to_char(date1, 'SYYYY-MM-DD') as date1, to_char(date2, 'SYYYY-MM-DD') as date2
from t
where (Date1 - Date2) / 365.25 >= 75;
DATE1 DATE2
----------- -----------
2016-06-15 1941-06-15
2016-06-15 1940-06-15
2016-06-15 1939-06-15
2016-06-15 1938-06-15
2016-06-15 1937-06-15
2016-06-15 1936-06-15
2016-06-15 -1947-06-15
So you can see the date from -1947 is shown, but the date from 1947 is not. And that is a lot more than 75 years ago:
select months_between(date '2016-06-15', date '-1947-06-15')/12 as years from dual;
YEARS
----------
3963
The reason adding to_date()
around your values stopped the record appearing is that you would be implicitly converting the date to a string and then back to a date, using the same MM/DD/YYYY
format mask. The intermediate string would not include the minus sign, so date '-1947-06-15'
would become the string 06/15/1947
. When that is converted back to a date the sign is still missing, and the string 06/15/1947
becomes date '1947-06-15'
. Which is now only 69 years ago, not 3963 years ago.
Upvotes: 1
Reputation: 1269773
Use the add_months()
function:
where date1 >= add_months(date2, 75*12)
The months calculations are more precise -- for years -- than the ones based on days.
Alternatively, you can use interval
:
where date1 >= date2 + interval '75' year
Upvotes: 1