MONTHS_BETWEEN('2018-12-31', '2019-02-28') outputs 1? - sql

Simply shown above, this has been causing inconsistencies with our data.
I'm currently writing an AMDP Logic that makes use of the difference between the months of each records, but surprisingly, the function MONTHS_BETWEEN() is not consistent for all cases and were seemingly skipping months for very specific cases. I don't know what is causing this. Is there a way to fix this inconsistency or some sort of alternate solution if none? Thanks.

Thanks to the comments, I figured out that the result from the function MONTHS_BETWEEN() is being calculated according to the number of days between. I initially assumed that it only uses the Month's value for the calculation, hence the expectation that Feb 2019 was 2 months away from Dec 2018.
Given that, I was able to come up with quite a fix that converts the dates into the first day of the month to get a guaranteed difference in months for any two given date values.
Here's a snippet of the SQL Code and the corresponding result:
This workaround is followed by an assumption that the dates being compared are all the last days of any given month. If that's not the case then a pre-processing by using the LAST_DAY() function will be needed to be inserted inside the NEXT_DAY() function in order for it to work similarly.
***EDIT:
Manually editing the string date value using LEFT() and CONCATENATING with '01' seems to be a more efficient and straightforward approach. I can't upload the screenshot but here's the working code:
SELECT MONTHS_BETWEEN(
LEFT('2018-12-31', 8) || '01',
LEFT('2019-02-28', 8) || '01'
) AS dmonths
FROM dummy

Related

Conversion failing for date formatting

I have an nvarchar(100) column which has a value ' 8/11/2022'.
I receive and error when trying to convert it to date...
select convert(date,[date],103)
from [Source].[TableName] s_p
--Msg 241, Level 16, State 1, Line 96
--Conversion failed when converting date and/or time from character string.
I have tried a number of different ways to approach but I can't find one to give me '08/11/2022'
select Date = REPLACE(LEFT([Date],10),' ','0')
from [Source].[TableName] s_p
--Outcome 8/11/2022
select REPLACE([DATE],' 8/','08/')
from [Source].[TableName] s_p
--Outcome 8/11/2022
select convert(nvarchar,[date],103)
from [Source].[TableName] s_p
--Outcome 8/11/2022
The strange thing is when I copy and paste from the results grid then do a replace it works fine...
select REPLACE(' 8/11/2022',' 8/','08/')
--Outcome 08/11/2022
Please help me to get to '08/11/2022' or any single digit to having a leading 0.
Thanks, Will
Different languages and cultures have their own formatting preferences around date values. Some places like M/dd/yyyy. Some places like dd/MM/yyyy. Or perhaps d-M-YYYY (different separators and conventions around leading zeros). The point is it's not okay to go into a place and impose our own preferences and norms on that culture.
The SQL language is no different. It really is it's own language, and as such has it's own expectations around date handling. If you violate these expectations, you should not be surprised when there are misunderstandings as a result.
The first expectation is for date and datetime values to be stored in datetime columns. It's hard to understate how much of a difference this can make for performance and correctness.
But let's assume that's not an option, and you have no choice but to use a string column like varchar or nvarchar. In that situation, there is still an expectation around how date values should be formatted.
Any database will do better if you use a format which stores the date parts in order by descending length. For example, ISO-8601 yyyy-MM-ddTHH:mm:sss[.fff] This is important to allow greater than/less than comparisons to work, it can greatly help with indexes and performance, and it makes cast/convert operations to datetime values MUCH more likely to succeed and be accurate.
For SQL Server specifically, there are three acceptable formats:
yyyy-MM-ddTHH:mm:sss[.fff],
yyyyMMdd HH:mm:ss[.fff], and
yyyyMMdd.
Anything else WILL have date values that don't parse as expected. Any string manipulation done to call the CONVERT() method should focus on reaching one of these formats.
With that in mind, and assuming 8/11/2022 means November 8 and not August 11 (given the 103 convert format), you need something like this:
convert(datetime,
right([date], charindex('/', reverse([date]))-1) -- year
+ right('0' + replace(substring([date], charindex('/', [date])+1, 2), '/', ''), 2) -- month
+ right('0' + left([date], charindex('/',[date])-1),2) -- day
)
And you can see it work here:
https://dbfiddle.uk/lM8sVySh
Yes, that's a lot of code. It's also gonna be more than a little slow. And again, the reason why it's so slow and complicated is you jumped in with your own cultural expectations and failed to respect the language of the platform you're using.
Finally, I need to question the premise. As the fiddle above shows, SQL Server is perfectly happy to convert this specific value without error. This tells me you probably have more rows, and any error is in fact coming from a different row.
With that in mind, one thing to remember is a WHERE clause condition will not necessarily run or filter a table before a CONVERT() operation in the SELECT clause. That is, if you have many different kinds of value in this table, you cannot guarantee your CONVERT() expression will only run on the date values, no matter what kind of WHERE clause you have. Databases do not guarantee order of operations in this way.
The problem could also be some invisible unicode whitespace.
Another possibility is date formats. Most cultures that prefer a leading day, instead of month or year, tend to also strongly prefer to see the leading 0 in the first place. That the zero is missing here makes me wonder if you might have a number of dates in the column that were formatted by, say, Americans. So then you try to parse a column with values both like 02/13/2022 and 13/02/2022. Obviously those dates can't both use the same format, since there is no 13th month.
In that case, best of luck to you, because you no longer have any way to know for certain whether 2/3/2022 means March 2nd or February 3rd... and trying to guess (by say, assuming your own common format) is just exacerbating the same mistake that got you into this mess in the first place.
It's worth noting all three of these possibilities would be avoided had you used DateTime columns from the beginning.
You'll want to use LPAD to add 0 to string, then CAST() string as date if you want to change to date data type

What is the purpose of the string "- 4/24" from a record that holds a date?

I have an already generated script that uses the following code: ORDER BY TO_CHAR((A.VERIFIED_DTTM - 4/24),'YYYY-MM-DD'). The output of A.VERIFIED_DTTM is a simple date eg: 09-SEP-19.
I was my understanding that 4/24 is the same as saying 4 hours. I have tried removing this string from my query and received radically different results (not just sorting or ordering).
In another area of the script, I have a code that makes excellent sense (A.VERIFIED_DTTM >= sysdate - 30). This I understand.
Can anyone explain what is actually happening with this query line?
Thank you!
It is subtracting 4 hours and converting the date value to a string in the format of YYYY-MM-DD. This is probably a timezone adjustment.
To be honest, I'm not sure why it doesn't just use:
ORDER BY A.VERIFIED_DTTM
Removing it should not radically change the results.

Grouping by month using a date column (DD.MM.YYYY HH24:MI:SS)

I am trying to create a monthly report for easy review and comparison of runtimes of certain processes.
The raw data needed for that is stored in a Oracle 11g database table, with the runtime being the difference between STARTDATE and ENDDATE which are both formatted as DD.MM.YYYY HH24:MI:SS.
One dateset is created for each run of a process, so there is a huge number of them stored in that table (several datasets per process per hour).
Now normally this would be pretty straight forward by just using a GROUP BY clause, however, I couldn't get it to work in this case. Most likely due to the DATE columns being involved with their special format.
Is there any possibility to group the data sets by the month which is stored within the STARTDATEcolumn? I already tried using some Oracle date functions which I found via googling, e.g. MONTH() and EXTRACT() , but so far I didn't get anywhere with it.
I would greatly appreciate any hints regarding this issue.
Thanks in advance and best regards,
daZza
To get a dates month in Oracle you can convert that part to a string and use this:
select
to_char(startdate, 'yyyymm') as startmonth,
....
from ...
group by to_char(startdate, 'yyyymm');

Date based on day of 365 Day Year

I have a date in the format [last two year][day of 365]. Using either SQL or excel functions, I need to determine the date this pertains to. The year is always the last two of the 2000s. There are no dates from before 2007. Is there a way to easily do this? I'm using this for a quick check, and am trying to avoid a large coding time for this.
In SQL, the date-handling functions seem to vary widely by SQL vendor, and there don't seem to be any good standard ways of handling date offsets. However, if you were a bit more specific about the specific SQL server you're using, someone might be able to provide an answer that applies.
In Excel, you can build a date for the first day of the year (e.g. 2008-01-01) and then add the number of days numerically, subtracting 1 so that the first day is still 2008-01-01; the resulting value, when formatted as a date, should be the precise date value. For example, =DATE(2008,1,1)-1+320 returns a date of 11/15/08 (at least in LibreOffice Calc).
You can try this formula in Excel where your data is in A1
=DATE(LEFT(A1,2)+100,1,RIGHT(A1,3))

Date Conversion with SQL Server/Reporting Services

I have 2 fields in the database month (numeric) and year (numeric) and I want to combine them in a report that combines those 2 fields and format them with MMM-YYYY. e.g 7-2008 becomes Jul-2008. How do I do that?
DateSerial is the correct answer:
http://msdn.microsoft.com/en-us/library/bbx05d0c(VS.80).aspx
SSRS uses VB.Net for expressions. Use the expression editor to browse the available functions, one of which is DateSerial.
To format the date, set the Format property on the textbox. You should be able to use "MMM-yyyy" as the format.
Update: As Peter points out, you would specify the parameters as needed. If you just care about year and month, just supply a value of 1 for the day. Since you are formatting the value without the day component it really doesn't matter what value you use (as long as it creates a valid date).
=DateSerial(year, month, day)
Brannon's answer is correct except that he omits the fact that you merely specify a literal for the day. Any value between 1 and 28 will do.