I have a table with couple of fields nvarchar(MAX)
which occupies date in dd/mm/yyyy
format.
I run this query:
SELECT CONVERT(datetime,[Start Date],103)
FROM [YellowCard_NewDesign].[dbo].[vw_All_Requests]
And I get error:
The conversion of a nvarchar data type to a datetime data type resulted in an out-of-range value.
The weird thing is that when I run this query like:
SELECT TOP 40 CONVERT(datetime,[Start Date],103)
FROM [YellowCard_NewDesign].[dbo].[vw_All_Requests]
It works but I don't want the TOP X
.
My table contains only 36 records. so i don't think that there is bad data.
20/03/2013
20/03/2013
10/03/2013
10/03/2013
11/03/2013
06/03/2013
06/03/2013
21/03/2013
12/03/2013
03/03/2013
18/03/2013
04/03/2013
28/02/2013
28/02/2013
28/02/2013
28/02/2013
31/01/2013
15/01/2013
23/01/2013
23/01/2013
31/01/2013
23/01/2013
30/01/2013
31/01/2013
24/01/2013
24/01/2013
24/01/2013
24/01/2013
24/01/2013
30/01/2013
23/01/2013
22/01/2013
23/01/2013
23/01/2013
23/01/2013
23/01/2013
Can someone help me with that?
The quick, easy, lazy way that will let you keep using a bad data type and allow all kinds of garbage into your table
SET DATEFORMAT DMY;
SELECT
-- other columns,
[Start Date] = CONVERT(DATETIME, CASE WHEN ISDATE([Start Date]) = 1
THEN [Start Date] END, 103)
FROM
dbo.vw_All_Requests;
Now, in this case it will return NULL
for any column value where it does not contain a valid date in d/m/y
format. If you want to exclude those rows instead of including them with NULL
values, you can add a WHERE
clause:
SET DATEFORMAT DMY;
SELECT
-- other columns,
[Start Date] = CONVERT(DATETIME, CASE WHEN ISDATE([Start Date]) = 1
THEN [Start Date] END, 103)
FROM
dbo.vw_All_Requests
WHERE ISDATE([Start Date]) = 1;
The reason you can't do this...
SELECT CONVERT(DATETIME, [Start Date], 103)
FROM ...
WHERE ISDATE([Start Date]) = 1;
...is because SQL Server might attempt the CONVERT
before the filter, resulting in the same error you're getting now. The CASE
expression (in most cases) allows you to control that evaluation order.
Now, of course, a date that is valid as d/m/y
format does not necessarily mean it was what the user intended. If you had an American enter 07/04/2013
for July 4th, you're going to incorrectly perceive that as April 7th. This is why regional formats like d/m/y
and m/d/y
are no good.
The quick, easy, lazy way that will let you keep using a bad data type but prevent more garbage from getting into your table with a little bit more work
You should still follow the advice above and fix or remove any data that doesn't conform, and at the very least add a check constraint so that no more junk gets into your table:
ALTER TABLE dbo.SourceTable
ADD CONSTRAINT CK_SillyMaxColumnIsValidDate
CHECK (CONVERT(DATE, [Start Date], 103) >= '0001-01-01');
So the following inserts will fail or succeed:
-- these succeed:
INSERT dbo.SourceTable([Start Date]) SELECT '01/01/2005';
INSERT dbo.SourceTable([Start Date]) SELECT '25/01/2005';
GO
-- fails:
INSERT dbo.SourceTable([Start Date]) SELECT '01/25/2005';
GO
-- fails:
INSERT dbo.SourceTable([Start Date]) SELECT 'garbage';
The failures prevent the bad inserts altogether, and only allow valid d/m/y
strings into the table. The error message is:
Msg 241, Level 16, State 1, Line 1
Conversion failed when converting date and/or time from character string.
The right way
First, identify the bad data:
SELECT [Start Date]
FROM dbo.vw_All_Requests
WHERE ISDATE([Start Date]) = 0;
Now, fix that data, wherever it comes from, so that you can fix the data type. This might mean a combination of these types of statements:
-- correct the date for specific bad values
UPDATE dbo.SourceTable
SET [Start Date] = '03/12/2012'
WHERE [Start Date] = 'whatever';
-- remove the value altogether for specific bad values
UPDATE dbo.SourceTable
SET [Start Date] = NULL
WHERE [Start Date] = 'whatever';
-- remove the row altogether for specific bad values
DELETE dbo.SourceTable
WHERE [Start Date] = 'whatever';
-- remove all rows with bad values
SET DATEFORMAT DMY;
DELETE dbo.SourceTable
WHERE ISDATE([Start Date]) = 0;
Then add a DATE
column to the source table:
ALTER TABLE dbo.SourceTable
ADD StartDate -- no space!
DATE;
Then update the data in that column:
UPDATE dbo.SourceTable
SET StartDate = CONVERT(DATETIME, [Start Date], 103);
Now drop the original column (you may need to adjust indexes that include this column or constraints that reference it):
ALTER TABLE dbo.SourceTable
DROP COLUMN [Start Date];
Now you can either change the new column name:
EXEC sp_rename N'dbo.SourceTable.StartDate', 'Start Date', 'COLUMN';
Or change the view:
ALTER VIEW dbo.vw_All_Requests
AS
SELECT
...
[Start Date] = StartDate
Or change your application to use the new, better column name. Don't forget to change all of your data type parameters to use the proper data type too, and when passing in string literals, STOP using regional formats like d/m/y
. Like @Kaf, I prefer yyyymmdd
, which can never be misinterpreted, but yyyy-mm-dd
will always work for DATE
- just not DATETIME
, example:
SET LANGUAGE FRENCH;
SELECT CONVERT(DATETIME, '2012-03-15');
Result:
Msg 242, Level 16, State 3, Line 2
La conversion d'un type de données varchar en type de données datetime a créé une valeur hors limites.
Some background
Please read this post:
As a final note
Please stop creating column names or aliases with spaces in them. If you need visual separation, use an underscore. These are both much better choices for column names, don't change the meaning in any way, and don't require ugly square brackets around every single reference:
Start_Date
StartDate
Saving Date in string type fields is not a good idea. But if that something you have to deal with at the moment, you could run this query with isdate()
function to check any bad data and fix them:
SELECT [Start Date]
FROM [YellowCard_NewDesign].[dbo].[vw_All_Requests]
WHERE isdate([Start Date]) = 0
Also, it would be better if you get the date in none culture specific
ISO format
before converting into Date type. If they are all in dd/mm/yyyy
format, you could get them in ISO format
( 'yyyymmdd'
) as below.
declare @d varchar(max) = '20/03/2013'
select convert(datetime,right(@d,4) + substring(@d,4,2) + left(@d,2))
So your actual query will be like:
SELECT CONVERT(datetime,right([Start Date],4) +
substring([Start Date],4,2) +
left([Start Date],2))
FROM [YellowCard_NewDesign].[dbo].[vw_All_Requests]
This has nothing to do with TOP 40. It means you dont have problematic data in top 40 rows.
If you have less data in your table, you can simply increase the limit to find out where the error is occuring
Ex:
SELECT TOP 100 CONVERT(datetime,[Start Date],103)
FROM [YellowCard_NewDesign].[dbo].[vw_All_Requests]
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.