June 19, 2016 at 9:06 am
Comments posted to this topic are about the item A SQL Server Date Handling Issue
June 20, 2016 at 5:42 am
I wouldn't call it a SQL Server issue. SQL Server expects the date to be passed in as "2016-06-20 00:00:00" which is the standard SQL Server datetime format. So if the application isn't doing that, then that is a programmer bug, not a SQL Server bug. Garbage in, garbage out.
June 20, 2016 at 6:59 am
trboyden (6/20/2016)
I wouldn't call it a SQL Server issue. SQL Server expects the date to be passed in as "2016-06-20 00:00:00" which is the standard SQL Server datetime format. So if the application isn't doing that, then that is a programmer bug, not a SQL Server bug. Garbage in, garbage out.
I couldn't agree more!
The OP shouldn't have gone as far as finding out about missing leap-years in 2002 or implicit datatype conversions from string to datetime, as interesting as these may be.
The statement WHERE CONVERT(CHAR(8), StartDate,108) BETWEEN FROM_TMS AND TO_TMS
is the true error - and a really stupid one at that!
It is clear from the example, that [FROM_TMS] and [TO_TMS] are not datetime, but TIME datatypes! Assuming that we can trust the name of StartDate to be a datetime field (or even a date field?), taking the first 8 characters from the left returns nonsense. Using "108" yields unexpected results, but no parameter-value would save the day. The programmer has failed the course of writing T-SQL code big time! And I am not sure the topic deserves a post in this forum...
EDIT:
My apologies to the OP!
dhaveedh (6/20/2016)
If you run the the following querySELECT CONVERT(CHAR(8), GETDATE(),108) : You will get data in the form hh:mi:ss
I committed the great sin of not looking up the value "108" in MSDN! Now I follow the thought of the original programmer (who - as many points out - is not the OP), and it is NOT stupid, nor an error.
It's just bad code. Because it relies on assumptions about the data types: As the OP points out (I just didn't saw it the other day), the programmer sees time values in a field and treats it like a time data type field, when in reality it is a character string.
The difference is crucial when we apply the CONVERT function, because it requires datetime in order for "108" to work. Unfortunately, an error isn't thrown by this syntax mistake. But on the other hand, this is also a strength of T-SQL: It can cope with dirty data without behaving like a spoiled child. Whether this is good or bad is subject to opinion, but precaution is necessary - that much is certain.
June 20, 2016 at 7:08 am
Thanks for the article! Often we're in a hurry to "make it work", and when we don't get the anticipated results, we just "try something else". It's almost always worth the time to understand why our code did something unexpected. 🙂
June 20, 2016 at 7:20 am
As expected a lot of people will be stuck on the fact that the code was wrong in the first place. That is not without question. However, the interesting thing to me was how SQL Server dealt with the issue, this is what I thought was worth sharing.
June 20, 2016 at 7:38 am
It's not so much a critique of the original code which I would expect you would understand needs refactoring. However, one should expect strange outcomes when data is passed to the system in a non-conforming manner. Again, the issue isn't with the SQL server software, it's just doing it's best to process the bad data being passed to it. A programmer should always be explicit in the data types and the data content being passed to the system. Leaving it up to the system to interpret bad data, is always a poor approach. There is no mystery here; as your own debug output shows, SQL told you how it was interpreting your data. It was up to you to transform it into the format it is expecting, to get the desired output. This is why I always recommend TDD (Test Driven Development). If you practice that faithfully, you won't ever have surprises, and you have the test cases built in to prove out your work.
June 20, 2016 at 8:34 am
The code mentioned here isn't the problem. The problem comes from the database design. Dates shouldn't be stored as strings or integers. We also need to know if the time and date are different attributes or a single one.
Having a good design, would allow to remove all functions from the join conditions and give better performance to the queries.
June 20, 2016 at 8:34 am
trboyden (6/20/2016)
It's not so much a critique of the original code which I would expect you would understand needs refactoring. However, one should expect strange outcomes when data is passed to the system in a non-conforming manner. Again, the issue isn't with the SQL server software, it's just doing it's best to process the bad data being passed to it. A programmer should always be explicit in the data types and the data content being passed to the system. Leaving it up to the system to interpret bad data, is always a poor approach. There is no mystery here; as your own debug output shows, SQL told you how it was interpreting your data. It was up to you to transform it into the format it is expecting, to get the desired output. This is why I always recommend TDD (Test Driven Development). If you practice that faithfully, you won't ever have surprises, and you have the test cases built in to prove out your work.
Garbage In, Garbage Out is a fact. However I believe whoever wrote the statement was expecting a Datetime value in the StartDate column, from which they wanted only the time portion using the parameter value 108.
As I said in the article, it was only when a leap year was encountered that the script failed. So it could have passed testing depending on what data was presented for testing.
If you run the the following query
SELECT CONVERT(CHAR(8), GETDATE(),108) : You will get data in the form hh:mi:ss
June 20, 2016 at 8:41 am
Changing the CONVERT to CHAR(11) instead of CHAR(8) does the trick. This, I guess, is the real error that should be solved.
SELECT CAST (CONVERT (CHAR(11), '29-Feb-2016', 108) as Date)
June 20, 2016 at 8:46 am
First convert the date to a date time and then convert the result produces the expected result.
SELECT CAST ('29-Feb-2016' as datetime)
SELECT CONVERT (CHAR(20), CAST ('29-Feb-2016' as datetime), 108)
SELECT CONVERT (CHAR(8), CAST ('29-Feb-2016' as datetime), 108)
June 20, 2016 at 12:04 pm
Luis Cazares (6/20/2016)
The code mentioned here isn't the problem. The problem comes from the database design. Dates shouldn't be stored as strings or integers. We also need to know if the time and date are different attributes or a single one.Having a good design, would allow to remove all functions from the join conditions and give better performance to the queries.
The db design is fine; as the data is stored as Time. The Stored Procedure and presumably the calling function are passing DateTime data as Varchar.
June 20, 2016 at 12:40 pm
Mad Myche (6/20/2016)
Luis Cazares (6/20/2016)
The code mentioned here isn't the problem. The problem comes from the database design. Dates shouldn't be stored as strings or integers. We also need to know if the time and date are different attributes or a single one.Having a good design, would allow to remove all functions from the join conditions and give better performance to the queries.
The db design is fine; as the data is stored as Time. The Stored Procedure and presumably the calling function are passing DateTime data as Varchar.
From the article:
the StartDate column was a VARCHAR(20)
So no, the db design is not fine. Even as datetime, it would be wrong if time is going to be used separate from the date.
June 20, 2016 at 1:09 pm
Luis Cazares (6/20/2016)
Mad Myche (6/20/2016)
Luis Cazares (6/20/2016)
The code mentioned here isn't the problem. The problem comes from the database design. Dates shouldn't be stored as strings or integers. We also need to know if the time and date are different attributes or a single one.Having a good design, would allow to remove all functions from the join conditions and give better performance to the queries.
The db design is fine; as the data is stored as Time. The Stored Procedure and presumably the calling function are passing DateTime data as Varchar.
From the article:
the StartDate column was a VARCHAR(20)
So no, the db design is not fine. Even as datetime, it would be wrong if time is going to be used separate from the date.
I'll concede that I should have read all of the specs, I completely missed that the text was coming from a second table.
StartDate I could go either way on; depending on other methods that may be calling the data
June 20, 2016 at 2:11 pm
LC
I quite agree that the table should not be using the varchar (20) field to store the date values.
What surprised me was how sql server interpreted the wrong data passed in. It did it's own conversion resulting in the selection of a Time_ID value of 1 as per the TIME table and only failed when it could not resolve it as a valid date in a year.
I thought it would not work outright.
June 20, 2016 at 7:00 pm
trboyden (6/20/2016)
SQL Server expects the date to be passed in as "2016-06-20 00:00:00"
That's not true at all. SQL Server will take a couple dozen date/time formats in stride with no explicit conversion at all, many of which aren't even listed in CONVERT. It certainly does NOT require that particular date format as an input to a date/time data type. This assumes the DATE FORMAT MDY setting.
Here's a simple test that proves at least 26 of the different possibilities.
--===== Create a test table to test different date formats with.
CREATE TABLE #DateTest (SomeDateTime DATETIME)
;
--===== Insert dates in 26 different formats with no explicit conversion
INSERT INTO #DateTest (SomeDateTime)
SELECT '6/10/1971' UNION ALL
SELECT '06/10/1971' UNION ALL
SELECT '6-10-1971' UNION ALL
SELECT '6.10.1971' UNION ALL
SELECT 'June 10 1971' UNION ALL
SELECT 'Jun 10 1971' UNION ALL
SELECT 'June 10, 1971' UNION ALL
SELECT 'Jun 10, 1971' UNION ALL
SELECT '10 June 1971' UNION ALL
SELECT '10 Jun 1971' UNION ALL
SELECT '1971-06-10' UNION ALL
SELECT '1971-6-10' UNION ALL
SELECT '1971/06/10' UNION ALL
SELECT '1971.06.10' UNION ALL
SELECT '19710610' UNION ALL
SELECT '6/10/71' UNION ALL
SELECT '06/10/71' UNION ALL
SELECT '6-10-71' UNION ALL
SELECT '6.10.71' UNION ALL
SELECT 'June 10 71' UNION ALL
SELECT 'Jun 10 71' UNION ALL
SELECT 'June 10, 71' UNION ALL
SELECT 'Jun 10, 71' UNION ALL
SELECT '10 June 71' UNION ALL
SELECT '10 Jun 71'
;
--===== They all "flew" and correctly created "1971-06-10 00:00:00.000"
SELECT * FROM #DateTest
;
If you're saying that the "2016-06-20 00:00:00" non ambiguous style is what should be passed, it's ok for humans but it's still not the ISO format.
--Jeff Moden
Change is inevitable... Change for the better is not.
Viewing 15 posts - 1 through 15 (of 38 total)
You must be logged in to reply to this topic. Login to reply