Months between not returning correct value - sql

Select MOD (RUNC (MONTHS_BETWEEN (TO_DATE ('28-Mar-2017', 'DD-MON-YYYY'),
TO_DATE ('28-Feb-2017', 'DD-MON-YYYY'))), 12) Months from dual
Why is this function returning 1 month even if not completed the month? Its supposed to be 0

You are wrong. MONTHS_BETWEEN treats the time span from date '2017-02-28' to date '2017-03-28' as exactly one month (same day in two adjacent months). This can be read in the docs: https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions089.htm
MONTHS_BETWEEN returns number of months between dates date1 and date2. If date1 is later than date2, then the result is positive. If date1 is earlier than date2, then the result is negative. If date1 and date2 are either the same days of the month or both last days of months, then the result is always an integer. Otherwise Oracle Database calculates the fractional portion of the result based on a 31-day month and considers the difference in time components date1 and date2.
One date is bigger than the other so you get a positive or negative number (i.e. not zero) depending on which is first and which is second parameter, and as the days equal, the result will be an integer. That is 1 or -1 for adjacent months.
Month calculation is a strange thing anyway, as a month is not a defined time span. You seem to have a certain definition in mind, which simply is different from how MONTHS_BETWEEN defines it. Nothing wrong about that. I would agree with MONTHS_BETWEEN in this case; from Feb 28 to Mar 28 is "exactly" one month. If you want different rules, then apply your own math :-)

Because it is working to its documented specification:
MONTHS_BETWEEN returns number of months between dates date1 and date2.
The month and the last day of the month are defined by the parameter
NLS_CALENDAR. If date1 is later than date2, then the result is
positive. If date1 is earlier than date2, then the result is negative.
If date1 and date2 are either the same days of the month or both last
days of months, then the result is always an integer. Otherwise Oracle
Database calculates the fractional portion of the result based on a
31-day month and considers the difference in time components date1 and
date2.
If you have requirements that are different to this then you will need to write your own function.

Related

SQL datediff translation

I am not a SQL user, it happens that I need to know what this type of SQL code means, can you help me please translating it to me?
CASE
WHEN DATEDIFF(to_date(B.DT_CAREPACK_END),
LAST_DAY(date_sub(add_months(current_date, 3), 20))) > 0
CASE statements let you introduce some conditional logic.
A full CASE sttatement would have a syntax like:
CASE
WHEN some logic is true THEN x
WHEN some other logic is true THEN y
ELSE THEN z
END as column_title
In your example, it doesn't look like you've provided the full statement as their is no END keyword.
Basically, this logic is checking when the difference between two dates (date-x and date-y) is positive or not. DATE_DIFF looks at the different between a start date (date-x) and an end date (date-y).
If date-x, the start date, is before date-y, the end date, then the result is positive. If the start date is after the end date, then the result is negative.
date-x is a date representation of the column DT_CAREPACK_END
date-y is taking the current_date, adding on 3 months (e.g. 4th September becomes 4th December), is is then subtracting 20 units (presumably days) and then setting that date to the last date of that month.
So, imagine DT_CAREPACK_END (presumably a date when something ends) is in the future and is 2022-10-02.
The inner logic here will take the current date (2022-09-04) and add 3 months to that date, making it 2022-12-04. Then, we are subtracting 20 days which is 2022-11-14. Then, we find the last day in that month, which would be 2022-11-30.
Finally, we look at the difference between 2022-10-02 (start date) and 2022-11-30 (end date). If that is a positive number, then the logic is satisfied. In this case, 2nd October is before 30th November, resulting in a positive logic and therefore the case logic is satisfied.
If the DT_CAREPACK_END is before the current_date logic, then it would be negative.
*N.B. I thought that date_add, date_sub and date_diff functions needed an interval unit to be explicitly stated (e.g. INTERVAL 20 DAY). I'm guessing the default here is days but that's an assumption on my part. I'm working in good-faith that the code snip is syntatically correct. *
Resources:-
Add Months:
https://docs.oracle.com/cd/B19306_01/server.102/b14200/functions004.htm
Date Sub:
https://docs.oracle.com/cd/E17952_01/mysql-5.7-en/date-and-time-functions.html#function_date-add
Last Day:
https://docs.oracle.com/cd/E17952_01/mysql-5.7-en/date-and-time-functions.html#function_last-day

Get Number of Months and Days Between Dates

In PL/SQL I have 2 dates and I need to find out the number of months between them and the days as well. For example date 1 is 1/10/2022 and date 2 is 2/12/2022 that would be 1 month and 2 days. I'm pretty secure in obtaining the number of months, but the days number has been a thorn in my side. Sometimes it comes out correct, sometimes it comes out short and other times it comes out too far. I would imagine it is because of the different number of days in the months, but I can't prove that just yet. Any help is appreciated.
Oracle provides a months_between function to do the calculation.
That isn't a good idea as the number of days in a month varies, it's not exactly known what the decimal part of the number represents.
select months_between(date '2022-04-03', date '2022-01-01') from dual;
MONTHS_BETWEEN(DATE'2022-04-03',DATE'2022-01-01')
3.06451612903225806451612903225806451613
If you assume every month has 30 days, then comparisons over larger date ranges (years) will be out by more and more days the larger the difference gets.
However, if you combine methods, using months_between to get the months, and then assume 30 days for a month to get the days part from the remainder, it's more consistent over longer periods…
with dates as (select date '2022-01-01' as date_from, date '2022-04-03' as date_to from dual)
select months_between(date_to, date_from) ,trunc(months_between(date_to, date_from)) as months ,round(mod(months_between(date_to, date_from),1)*30) as days
from dates
MONTHS_BETWEEN(DATE_TO,DATE_FROM) MONTHS DAYS
3.06451612903225806451612903225806451613 3 2

SQL ORACLE Get week numbers from multiple datetime rows

I have 70.000 rows of data, including a date time column (YYYY-MM-DD HH24-MM-SS.).
I want to split this data into 3 separate columns; Hour, day and Week number.
The date time column name is 'REGISTRATIONDATE' from the table 'CONTRACTS'.
This is what I have so far for the day and hour columns:
SELECT substr(REGISTRATIONDATE, 0, 10) AS "Date",
substr(REGISTRATIONDATE, 11, 9) AS "Hour"
FROM CONTRACTS;
I have seen the options to get a week number for specific dates, this assignment concerns 70.000 dates so this is not an option.
You (the OP) still have to explain what week number to assign to the first few days in a year, until the first Monday of the year. Do you assign a week number for the prior calendar year? In a Comment I asked about January 1, 2017, as an example; that was a Sunday. The week from January 2 to January 8 of 2017 is "week 1" according to your definition; what week number do you assign to Sunday, January 1, 2017?
The straightforward calculation below assigns to it week number 0. Other than that, the computation is trivial.
Notes: To find the Monday of the week for any given date dt, we can use trunc(dt, 'iw'). iw stands for ISO Week, standard week which starts on Monday and ends on Sunday.
Then: To find the first Monday of the year, we can start with the date January 7 and ask for the Monday of the week in which January 7 falls. (I won't explain that one - it's easy logic and it has nothing to do with programming.)
To input a fixed date, the best way is with the date literal syntax: date '2017-01-07' for January 7. Please check the Oracle documentation for "date literals" if you are not familiar with it.
So: to find the week number for any date dt, compute
1 + ( trunc(dt, 'iw') - trunc(date '2017-01-07', 'iw') ) / 7
This formula finds the Monday of the ISO Week of dt and subtracts the first Monday of the year - using Oracle date arithmetic, where the difference between two dates is the number of days between them. So to find the number of weeks we divide by 7; and to have the first Monday be assigned the number 1, instead of 0, we need to add 1 to the result of dividing by 7.
The other issue you will have to address is to convert your strings into dates. The best solution would be to fix the data model itself (change the data type of the column so that it is DATE instead of VARCHAR2); then all the bits of data you need could be extracted more easily, you would make sure you don't have dates like '2017-02-29 12:30:00' in your data (currently, if you do, you will have a very hard time making any date calculations work), queries will be a lot faster, etc. Anyway, that's an entirely different issue so I'll leave it out of this discussion.
Assuming your REGISTRATIONDATE if formatted as 'MM/DD/YYYY'
the simples (and the faster ) query is based ond to to_char(to_date(REGISTRATIONDATE,'MM/DD/YYYY'),'WW')
(otherwise convert you column in a proper date and perform the conversio to week number)
SELECT substr(REGISTRATIONDATE, 0, 10) AS "Date",
substr(REGISTRATIONDATE, 11, 9) AS "Hour",
to_char(to_date(REGISTRATIONDATE,'MM/DD/YYYY'),'WW') as "Week"
FROM CONTRACTS;
This is messy, but it looks like it works:
to_char(
to_date(RegistrationDate,'YYYY-MM-DD HH24-MI-SS') +
to_number(to_char(trunc(to_date(RegistrationDate,'YYYY-MM-DD HH24-MI-SS'),'YEAR'),'D'))
- 2,
'WW')
On the outside you have the solution previous given by others but using the correct date format. In the middle there is an adjustment of a certain number of days to adjust for where the 1st Jan falls. The trunc part gets the first of Jan from the date, the 'D' gets the weekday of 1st Jan. Since 1 represents Sunday, we have to use -2 to get what we need.
EDIT: I may delete this answer later, but it looks to me that the one from #mathguy is the best. See also the comments on that answer for how to extend to a general solution.
But first you need to:
Decide what to do dates in Jan before the first Monday, and
Resolve the underlying problems in the date which prevent it being converted to dates.
On point 1, if assigning week 0 is not acceptable (you want week 52/53) it gets a bit more complicated, but we'll still be able to help.
As I see it, on point 2, either there is something systematically wrong (perhaps they are timestamps and include fractions of a second) or there are isolated cases of invalid data.
Either the length, or the format, or the specific values don't compute. The error message you got suggests that at least some of the data is "too long", and the code in my comment should help you locate that.

How to get the difference between two dates (informix)?

How to get the difference between two dates (informix) in integer format like that
day = 15
mon = 2
year = 1
There are two sets of date/time values in Informix: DATE and DATETIME.
The DATE type is oldest (it was in the precursor to the SQL-based Informix), and represents the integer number of days since a reference date (where day 0 is 1899-12-31, so day 1 was 1900-01-01).
You get the difference between two DATE values in days by subtracting one from the other.
The DATETIME system is newer (but still old — circa 1990). You can take the difference between two DATETIME YEAR TO DAY values and get a result that is an INTERVAL DAY TO DAY (essentially the number of days).
You could also take the difference between two DATETIME YEAR TO MONTH values and get a result that is an INTERVAL YEAR TO MONTH.
However, there is no way to get a difference in years, months and days because there is no simple way to deduce that value. In fact, ISO SQL recognizes two classes of INTERVAL: those in the YEAR-MONTH group, and those in the DAY-SECOND group. You can't have an INTERVAL that crosses the MONTH/DAY barrier.
Use the MDY function :
select mdy(2,15,2014) - mdy(1,15,2014) from sysmaster:sysdual

Calculate year from date difference in Oracle

I want to calculate the number of years between two dates.
eg :- Select to_date('30-OCT-2013') - TO_date('30-SEP-2014') FROM DUAL;
This would result to 335 days. I want to show this in years, which will be .97 years.
Simply do this(divide by 365.242199):
Select (to_date('30-SEPT-2014') - TO_date('30-OCT-2013'))/365.242199 FROM DUAL;
1 YEAR = 365.242199 days
OR
Try something like this using MONTHS_BETWEEN:-
select floor(months_between(date '2014-10-10', date '2013-10-10') /12) from dual;
or you may also try this:-
SELECT EXTRACT(YEAR FROM date1) - EXTRACT(YEAR FROM date2) FROM DUAL;
On a side note:-
335/365.242199 = 0.917199603 and not .97
I don't know how you figure that's .97 years. Here's what I get:
SQL> SELECT ( TO_date('30-SEP-2014') - to_date('30-OCT-2013')) /
(ADD_MONTHS(DATE '2013-10-30',12) - DATE '2013-10-30') "Year Fraction"
FROM DUAL;
Year Fraction
-------------
0.91780821917
You're going to have to pick a date to base your year calculation on. This is one way to do it. I chose to make a year be the number of days between 10/30/2013 and 10/30/2014. You could also make it a year between 9/30/2013 and 9/30/2014.
As an aside, if you're only interested in 2 decimal places, 365 is pretty much as good as 366.
UPDATE: Used ADD_MONTHS in calculating the denominator. That way you can use the same date for the entire calculation of the number of days in a year.
None of the methods proposed in the other answers give exactly the same answer, look:
with dates as ( select to_date('2013-10-01', 'YYYY-MM-DD') as date1, to_date('2014-09-01', 'YYYY-MM-DD') as date2 from dual)
select months_between(date2, date1)/12 as years_between, 'months_between(date1, date2)' as method from dates
union
select (date2 - date1)/365.242199, '(date2 - date1) / 365.242199' from dates
union
select extract(year from date2) - extract(year from date1), 'extract(year) from date2 - extract(year from date1)' from dates
union
select (date2 - date1) / (ADD_MONTHS(date1 ,12) - date1), '(nb days between date1 and date2) / (nb days in 1 year starting at date1)' from dates
;
gives
YEARS_BETWEEN METHOD
0.9166666666666666666666666666666666666667 months_between(date1, date2)
0.9171996032145234127231831719422979380321 (date2 - date1) / 365.242199
0.9178082191780821917808219178082191780822 nb days date2-date1 / (nb days in 1 year starting at date1)
1 extract(year) from date2 - extract(year from date1)
Why? Because they are all answering slightly different questions.
MONTHS_BETWEEN gives the number of whole months between the 2 dates, and calculates the fractional part as the remainder in days divided by 31.
dividing by 365.242199 assumes that you want the number of solar years between 00:00 on the first date and 00:00 on the second date, to 9 significant figures.
the third method assumes you want to calculate how many calendar days between the two dates, relative to the number of calendar days in the specific year that started on the first date (so the same number of calendar days will give you a different number of years, depending on whether there's a leap day between date1 and the same date on the following year).
the extract(year) approach assumes you want know the difference in whole numbers between the calendar year of the first date and the calendar year of the second date
It's not possible to answer the question perfectly, without knowing which kind of year we are talking about. Do we mean a solar year, or a calendar year, and if we mean a calendar year, do we we want to calculate by months (as if all months were the same length, which they aren't) or by the actual number of days between those dates and in that specific year?
Indeed, if we're talking about calendar years, it's not possible to calculate a fractional number of years in a consistent way at all, since the concept "calendar year" doesn't correspond to a fixed number of days.
The good news is that (aside from the fourth method) all the approaches give the same answer to the first 2 significant figures, as DCookie said. So you can save worrying about what you mean when you say "year", and instead start to think of other concerns such as performance, portability, readability... which also are quite different between these approaches.
I do think though, that whenever a non-programmer asks for something like "the fractional number of years between two dates," they should be punished by being given a detailed explanation of the different ways to calculate it, and why and how they are different, until they agree that it would be better expressed in number of weeks (which at least have the benefit of containing a fixed number of days).