I have a table which has a DATETIME column called dtInsertDate.
The value in this column is '2012-08-10 22:48:41.047'. I know that this date is 10th August 2012.
I want to run the following SELECT statement to retrieve this row:
select *
from myTable
where dtinsertdate = '2012-08-10 22:48:41.047'
The puzzling thing is, if I run this SELECT when the option dateformat is mdy, it finds that row.
If I run it on another copy of the database when the option dateformat is dmy, the SELECT doesn't return anything.
Why is this ?
The reason is that dmy causes your string literal to be interpreted as day first - so that is October 8th instead of August 10th.
The solution is to always use an unambiguous format for date literals (or to use properly typed parameters instead of dealing with translating from a string in the first place).
WHERE dtinsertdate = '20120810 22:48:41.047';
As you've discovered, those dashes in your date can make the actual value be interpreted differently depending on language / dateformat settings.
Another alternative is to keep the dashes but to inject a T instead of a space, e.g.:
WHERE dtinsertdate = '2012-08-10T22:48:41.047';
Your original is more readable, but it's only guaranteed to work if you make the language and dateformat fixed (for example try with SET LANGUAGE FRENCH;). Try this experiment:
SET DATEFORMAT DMY;
DECLARE #d DATETIME = '2012-08-10 22:48:41.047'
SELECT MONTH(#d);
GO
SET LANGUAGE FRENCH;
DECLARE #d DATETIME = '2012-08-10 22:48:41.047'
SELECT MONTH(#d);
GO
SET DATEFORMAT MDY;
DECLARE #d DATETIME = '2012-08-10 22:48:41.047'
SELECT MONTH(#d);
GO
SET DATEFORMAT MDY;
SET LANGUAGE ENGLISH;
DECLARE #d DATETIME = '2012-08-10 22:48:41.047'
SELECT MONTH(#d);
GO
Sometimes the result is 8, sometimes the result is 10. Now try it again with one of the two formats I suggest above - result is always 8.
I have lots of juicy details worthy of reading here:
Bad habits to kick : mis-handling date / range queries
But for the specific problem you're talking about, always, always, always use one of the following formats for date/time literals, I don't consider any others safe.
For date + time:
YYYY-MM-DDTHH:MM:SS...
YYYYMMDD HH:MM:SS...
For date only:
YYYYMMDD
Related
If I run the query:
select startdate, count(*)
from tablename
where startdate > '2020-04-06'
It only returns value where the startdate is after 4th June 2020. However the dates in the table are in the format YYYY-MM-DD HH:mm:ss.sss.
If I run a getdate() or sysdatetime() it returns 2020-06-16 14:29:29.157 in the correct format.
So why is the query using YYYY-DD-MM? And how do I get it to change by default?
P.S. I'm aware that I could use CONVERT or FORMAT in the query, but as all dates will be in the YYYY-MM-DD format I'd like that to be the default, and not have to write extra code each time.
EDIT: I'm using Microsoft SQL Server Management Studio
EDIT2: I checked with a colleague and the same thing happens to them.
That depends on various settings. You can get around this by removing the hyphens:
startdate > '20200406'
In SQL Server, this format is always unambiguous, YYYYMMDD. I prefer the version with the hyphens, because it is more standard. But if you are dealing with this as an issue I would suggest using the SQL Server unambiguous format.
You can handle it in two ways:
At the session level. you can set format and issue query
Use ISO 8601 format (Recommended)
DECLARE #table table(a datetime)
INSERT INTO #table values('2020-04-06')
SELECT * FROM #table WHERE A = '2020-04-06' -- ISO 8601
set dateformat ymd
SELECT * FROM #table WHERE A = '2020-04-06' -- Format change
I am using SQL server. We have date column in table but some of them are stored in different format.
For e.g: We have records with format 2015-12-09 00:00:12.000 but some records are there with format as 2015/09/09 00:08:09.000 or any other valid date type.
How can I identify records with different date format from table.
I tried using isdate() function but as all date are valid there is no luck.
Can you please guide me with this.
Solution part 1
Although it is possible that this solution might not completely solve your issue, if at least you can unify your dates to look similar to each other then your position should be very much improved by placing the following at the top of your query:
SET DATEFORMAT DMY
OR
SET DATEFORMAT YMD
OR
SET DATEFORMAT MDY
Example
SELECT
[date]
FROM
[your_table]
Points of note:
M means month, D means day and Y means year.
Setting the DATEFORMAT affects both how dates appear in the result set and how dates are converted to VARCHAR and similar.
If you don't set the DATEFORMAT then running the same stored procedure on different machines/set-ups can yield differing results.
Solution part 2
You can also perform some string manipulation to replace -, /, etc. with the character of your choice.
..Put the following at the top of your query:
DECLARE #DateSeparator NVARCHAR(1) = '/'
..Use the following as part of your select statement:
REPLACE(REPLACE([date], '/', #DateSeparator), '-', #DateSeparator)
Example
SELECT
REPLACE(REPLACE(CONVERT(NVARCHAR(20), [date]), '/', #DateSeparator), '-', #DateSeparator) AS [date]
FROM
[your_table]
Points of note:
In the above example, the date separator is set to /, but change the value of #DateSeparator to the date separator of your choice.
Both solutions combined
Example
SET DATEFORMAT YMD
SELECT
REPLACE(REPLACE(CONVERT(NVARCHAR(20), [date]), '/', #DateSeparator), '-', #DateSeparator) AS [date]
FROM
[your_table]
Points of note:
Replace [your_table] with the name of your source table.
Since the dates were stored as VARCHAR instead of DATETIME (as they should have been) then there really is no way through SQL to determine it. Is the date '2016-02-01' February 1st or January 2nd? If you can't tell then how is any computer code going to figure it out?
Your best bet is to go back through the application(s) that have inserted or updated data in the table and try to figure out what they might have used. If users were just typing in data then that's unlikely to help, although maybe you can look for some consistencies - Janice always enters data MM/DD/YYYY, but Pierre always puts it in as DD/MM/YYYY, etc. That's probably your best bet in narrowing it down. Otherwise, you might look at other data in your system - if the table also has an inserted_on column for example, then maybe that and your other date would usually be within a few days of each other, etc.
We've recently migrated our database to a different server and since this I think the date format querying has changed somehow.
Previously we could use the following..
SELECT * FROM table WHERE date > 'YYYY-MM-DD'
However now we have to use..
SELECT * FROM table WHERE date > 'YYYY-DD-MM'
Can someone tell me what I need to change to get back to the previous version?
Try this one -
Query:
SET DATEFORMAT ymd
Read current settings:
DBCC USEROPTIONS
Output:
Set Option Value
-------------------------- -----------------
...
language us_english
dateformat ymd
...
You are right, the date format is different between the servers.
Lots of people fall into the trap of assuming that if you specify a date literal as 'YYYY-MM-DD', it will be interpreted as that regardless of the current date format. This is incorrect. SQL Server sees the 4 digits at the start of the string and correctly deduces that they represent the year. However, it then uses the current date format to tell which way round the month and day are. If you are in the UK, for example, this puts you in an awkward situation because you need a date format of DMY to interpret a date literal like 'DD-MM-YYYY', but a date format of MDY to interpret a date literal like 'YYYY-MM-DD'.
You have several options:
SET DATEFORMAT YMD, and don't let users enter dates any other way.
Use the ODBC date literal syntax {d'YYYY-MM-DD'}. This will be parsed correctly regardless of the current date format. CONVERT(DATE, 'YYYY-MM-DD', 120) has the same effect.
Remove all literal values from your queries and use parameters instead. This is by far the best alternative, and I strongly recommend it.
is you use different formats for the string then you can avoid this behaviour.
There are 2 iso formats that are always specific -- sql server will always parse them in the same way regardless of the server date format setting.
These are:
1) Short form : YYYYMMDD. Example '20120301' -- 1st March 2012
2) Long Form : YYYY-MM-DDTHH:MM:SS.msms'. Example '2012-03-01T12:13:00.000Z' -- 1st March 2012 at 13 minutes past 12 (PM)
In the long form the miliseconds is optional -- this is a perfectly acceptable ISO datetime '2012-03-01T12:13:00Z'
The Z at the end is time zone information. SQL Server doesn't actually require this. (though other products are a bit more exacting)
Try this for example:
DECLARE #foo DATETIME
SET DATEFORMAT DMY
-- this will be the 3rd of january in DMY
SET #foo = '2012-03-01'
SELECT 'DMY: Not ISO', #foo
SET #foo = '20120301'
SELECT 'DMY: ISO', #foo
SET DATEFORMAT MDY
-- this will be the 1st of March in MDY
SET #foo = '2012-03-01'
SELECT 'MDY: not ISO', #foo
SET #foo = '20120301'
SELECT 'MDY: ISO', #foo
When you use text to enter dates you should always try to use one of the two ISO standards. It just makes things much more deterministic.
Short format (SQL Server)
http://msdn.microsoft.com/en-US/library/ms187085(v=sql.90).aspx
ISO 8601 Format (SQL Server)
http://msdn.microsoft.com/en-us/library/ms190977(v=sql.90).aspx
It's a matter of language/culture
Set Language 'us_english'
I always use this code for conversion in datetime:
DECLARE #a datetime
SET #a= CONVERT(datetime,'2012-12-28 14:04:43')
print #a
But this does not work anymore! I tried even restarting SQL Server, but the problem remains:
The error in the image is in Italian. In English should be:
The conversion of a char data type to datetime resulted in a datetime
value that is out of range of allowed values.
There are many formats supported by SQL Server - see the MSDN Books Online on CAST and CONVERT. Most of those formats are dependant on what settings you have - therefore, these settings might work some times - and sometimes not.
The way to solve this is to use the (slightly adapted) ISO-8601 date format that is supported by SQL Server - this format works always - regardless of your SQL Server language and dateformat settings.
The ISO-8601 format is supported by SQL Server comes in two flavors:
YYYYMMDD for just dates (no time portion); note here: no dashes!, that's very important! YYYY-MM-DD is NOT independent of the dateformat settings in your SQL Server and will NOT work in all situations!
or:
YYYY-MM-DDTHH:MM:SS for dates and times - note here: this format has dashes (but they can be omitted), and a fixed T as delimiter between the date and time portion of your DATETIME.
This is valid for SQL Server 2000 and newer.
If you use SQL Server 2008 or newer and the DATE datatype (only DATE - not DATETIME!), then you can indeed also use the YYYY-MM-DD format and that will work, too, with any settings in your SQL Server.
Don't ask me why this whole topic is so tricky and somewhat confusing - that's just the way it is. But with the YYYYMMDD format, you should be fine for any version of SQL Server and for any language and dateformat setting in your SQL Server.
So in your concrete case - use this:
DECLARE #a datetime
SET #a= CONVERT(datetime, '2012-12-28T14:04:43')
print #a
and this should work on any SQL Server installation, with any language and date format settings.
If you run your original code for US English - it will work just fine:
SET LANGUAGE English
DECLARE #a datetime
SET #a= CONVERT(datetime, '2012-12-28 14:04:43')
print #a
Dec 28 2012 2:04PM
but if you use Italian (or German, or British, or French) as your language, it will fail because the format without the T in the middle of the date/time string is NOT language-independent and not "safe" :
SET LANGUAGE Italian
DECLARE #a datetime
SET #a= CONVERT(datetime, '2012-12-28 14:04:43')
print #a
Msg 242, Level 16, State 3, Line 4
La conversione di un tipo di dati varchar in datetime ha generato un valore non compreso nell'intervallo dei valori consentiti.
You are trying to convert a string to a datetime. Problem is with the date part. Best way is to get the date string into ISO format (yyyymmdd) and then convert. Try this;
DECLARE #a datetime
SET #a= CONVERT(datetime,replace('2012-12-28 14:04:43', '-',''))
print #a
My guess is that the default date time format changed on your computer. Add the conversion specification back in:
SET #a= CONVERT(datetime,'2012-12-28 14:04:43', 121)
I have tables with this type of datetime: 2010-09-16 00:32:41.960' CreatedDate column.
I need to perform a query ... where [CreatedDate] >= '2010-09-16 00:32:41.960'
but that just gives me
The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
What is the correct syntax, it's been a while since I've done SQL the last time.
Thanks in advance.
Use an unambiguous datetime format so SQL Server doesn't have to guess how to convert it:
where [CreatedDate] >= '2010-09-16T00:32:41.960'
It's a shame that, depending on your regional settings, it may interpret the form with a space separator (rather than T) as YYYY-DD-MM hh:mm:ss. Which gives an out of range month with your example (and wrong results for dates early in the month, except when day=month)
I'd normally list the safe formats as:
YYYYMMDD
YYYY-MM-DD'T'hh:mm:ss
YYYY-MM-DD'T'hh:mm:ss.mil
There are some other formats that are now safe if converting to datetime2 or date, but I can't remember them, and the above usually suffice.
Another alternative would be to run a set dateformat statement before using these date literals:
set dateformat mdy
select MONTH('2010-09-16 00:32:41.960')
Gives 9 as the result, whereas what you're experiencing can be reproduced with:
set dateformat dmy
select MONTH('2010-09-16 00:32:41.960')
Try:
where [CreatedDate] >= CONVERT(datetime,'2010-09-16 00:32:41.960');