I realise that you can use SET DATEFORMAT to alter the way that a string is parsed but is the following unambiguous:
SELECT CONVERT(DATE, '02Oct15')
Is there any circumstances that this would be interpreted as 15th October 2002?
The century conversion will be an issue in 35 years (the y2k50, there is an amusing thought), but until then it's unambiguous. Any attempt to pass an invalid date in the first section results in a conversion error rather than it treating the third section as a date instead of a year.
Finally changing the default language (which changes the default date format) uses the same conversion (British), or causes it error out (Italian). Still as others have mentioned, storing dates in this format is a bad idea, however unless I miss my guess this seems like your importing data from a report, so you probably don't have much control over it. Anyway hope that helps and good luck.
I was not able to make 02Oct15 to become October 15, 2002. However, if all three date components are numbers, it's easy to make that cast:
SET DATEFORMAT YMD
SELECT CONVERT(DATE, '02Oct15'), -- October 2, 2015
CONVERT(DATE, '021015') -- October 15, 2002
I suspect that once you introduce natural language to the string, SQL Server use culture-specific rules to convert the date components. But in the end, only Microsoft knows what SQL Server's string-to-date algorithm is.
Related
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
Trying to convert a datetime format column (example value: 12-11-2020 18:15:06) which is actually a nvarchar into this date format: yyyymmdd
This is what I tried so far but I'm getting the following error:
What am I doing wrong?
There are many problems here.
Dates should not be stored as strings.
You lose the ability to perform any kind of date math or extract date parts.
You lose built-in validation (both invalid dates like February 31st and any garbage that doesn't even look like a date).
For example, we have no idea if 12-11-2020 is December 11th or November 12th, or if the data was entered consistently. Imagine a person from the US did some of the data entry and a colleague from Germany did the rest.
FORMAT() is the most expensive way to format a date (see this and this - these are articles about removing time altogether, but the overhead with FORMAT() is the same).
An index on MYDATE can't be used to satisfy this query, so you will do a full table scan every time, which will get worse and worse as the table grows.
You can perform your query in your given scenario without changing anything, but I highly recommend you fix the column and make sure data entry can't be arbitrary (use a date picker or calendar control so you dictate a consistent format).
If 12-11-2020 is December 11th:
WHERE TRY_CONVERT(date, MYDATE, 110) >= #DateVariable;
If 12-11-2020 is November 12th:
WHERE TRY_CONVERT(date, MYDATE, 105) >= #DateVariable;
Note that this still might not get the correct and logical results if some people thought they entered December 11th and others thought they entered November 12th.
You can see all the valid style numbers for CONVERT/TRY_CONVERT here.
CONVERT(VARCHAR(11),Enrollment.[Enroll End],106)
I thought the above was supposed to return a date like '12 Jan 2016', it is instead returning dates like '15 Oca 2016'. Does anyone know why this is happening? I originally thought maybe the language was set incorrectly but it was not.
https://msdn.microsoft.com/en-us/library/ms187928.aspx denotes 106 with this
1 These style values return nondeterministic results. Includes all (yy) (without century) styles and a subset of (yyyy) (with century) styles.
I'm not sure if the above has something to do with it - though I am not entirely sure what they mean by nondeterministic results
Thanks for the help!
The values returned are non-deterministic because they rely on the locale settings of the user connection. In other words, you can run the same exact code from two different computers and get different results - you can't determine the result based solely on the parameters.
Make sure that your client machine is set to an English locale.
I hope this helps..
if local setting are different. always use dateformat
set dateformat dmy
declare #date datetime = '12-01-2016'
select CONVERT(VARCHAR(11),#date,106)
I need to import some data obtained from an Indonesian file, part of which is a date in the form '01OKT2011'. I need to be able to automatically convert these to a T-SQL datetime, eg 10-01-2011 00:00:00.
The simplest way I can think of to do it is to create a lookup table with a column for the foreign short name and one for the month number, but this is made more difficult by the fact that the only source file I have at the moment only covers October and November, and I'm not entirely sure how the other months will be abbreviated.
Is there a simpler way to do this? Does one of the built in languages in SQL Server use the same month short names?
Look into your syslanguages table to learn whether your SQL Server installation supports Indonesian.
Supposing that SQL Server supports Indonesian, set your language to it. Also, SET DATEFORMAT ymd. Then use the datetimes directly.
If Indonesian is not supported/installed, you should try hard to solve localization outside of the database (which I'd actually recommend even if the database server is willing to understand your month names, but I'm saving that advice for a quite different question).
In that case the following approach might help you, if not for actual use, at least for conversion of your data.
DATETIMEFROMPARTS(
SUBSTRING(x, 6, 4),
REPLACE(...(REPLACE(SUBSTRING(x, 3, 3), 'JAN', 'Jan')...,'OKT', 'Oct'),...,'DEC', 'Dec')...),
SUBSTRING(x, 1, 2)
)
I require a SQL script to validate a VARCHAR field in a table in a SQL Server 2005 database that contains DateTime values, in the format DD/MM/YYYY, or NULL values. I would like to identify all invalid dates. Can anyone suggest a method?
UPDATE
The answer has to make use of T-SQL; for performance reasons, I can't make use of SQLCLR.
Thanks, MagicAndi
Use "ISDATE()" OR "IS NULL": but set the language first to recognise the day-month-year order
SET LANGUAGE british
SELECT ISDATE('12/31/2009'), ISDATE('31/12/2009')
SET LANGUAGE us_english
SELECT ISDATE('12/31/2009'), ISDATE('31/12/2009')
Edit: As mentioned by #edosoft, you can use SET DATEFORMAT too. SET LANGUAGE implicitly sets DATEFORMAT, SET DATEFORMAT overrides SET LANGUAGE
You should specify the dateformat when using ISDATE(). From Books Online:
SET LANGUAGE us_english;
SET DATEFORMAT dmy;
SELECT ISDATE('15/04/2008'); --Returns 1.
You could use the ISDATE() function
you can use the inbuilt T-SQL IsDate() function.
and change the column to be datetime not varchar.
you can't sort or do any date calcualtions on a varchar column.
SQL's date handling isn't great, we wrote a DotNet function to do our DateTime conversions for difficult case.
I have a solution, although it applies only in a certain set of circumstances. Your case may or may not be right for this.
Create a table called ALMANAC with one row for every valid date. You can populate it with ten years of valid dates with only 3,653 rows, more or less. Even if you go for a hundred years, that's still only 36,525 rows, not too terribly big by today's standards.
You can add strange company specific attributes to this table if it's useful. These are attributes like whether the date is a company specific holiday or not, and what fiscal week, fiscal month, fiscal quarter, and fiscal year the date belongs to.
You'll have to write a program to populate this table, and this program will have to have your company specific calendar rules embedded, if you choose the extra attributes.
Then, you can use this table just like you would use any code validation table. You may also be able to use it to make quirky calendar driven reporting criteria absolutely trivial to implement.
This is only a good idea if your dates are limited to a limited time span, like ten years or maybe even a hundred years. But sometimes, it can really be a time saver! I have sometimes used this technique to make my database independent of one vendor's calendar functions.