Strange issue when converting VarChar(max) to DATE or DATETIME in SQL Server 2008
SELECT Files.URL, Files.MD5, Files.Thumbnail, Files.Title, CONVERT(DATE, AttributeData.Keyword) AS DateUpdated, Attributes.Name AS DateType,
Metadata.metadata
FROM Files INNER JOIN
Metadata ON Files.ID = Metadata.FileID INNER JOIN
FilesToAttributeData ON Files.ID = FilesToAttributeData.FileID INNER JOIN
AttributeData ON FilesToAttributeData.AttributeDataID = AttributeData.ID INNER JOIN
Attributes ON AttributeData.AttributeID = Attributes.ID
WHERE 开发者_如何学C (Files.GeneralSearch = 1) AND
(Attributes.Name = 'Process Date' OR Attributes.Name = 'Publish Date') AND
(ISDATE(AttributeData.Keyword) = 1) AND
(CONVERT(DATE, AttributeData.Keyword) > DATEADD(DAY, - 90, GETDATE()))
ORDER BY DateUpdated DESC
This is my original sql query. It seems to work fine with our data in our dev environment. In production however I get the following error. "Msg 241, Level 16, State 1, Line 1 Conversion failed when converting date and/or time from character string.". Now if I remove the convert function in the SELECT and just output AttributeData.Keyword it will work fine. If I leave that alone and remove the convert function in the where clause it will work fine. It will not work if they both exist though.
Any ideas what could cause this? I have tried CAST and I have tried using a specific date style. Our dates generally look like yyyymmdd. An example is '20110318'. If I replace AttributeData.Keyword with this string it will also fail. I really have no idea what is going on here.
Here is an example of a query that works.
SELECT (AttributeData.Keyword) AS DateUpdated, Attributes.Name AS DateType
FROM Files INNER JOIN
Metadata ON Files.ID = Metadata.FileID INNER JOIN
FilesToAttributeData ON Files.ID = FilesToAttributeData.FileID INNER JOIN
AttributeData ON FilesToAttributeData.AttributeDataID = AttributeData.ID INNER JOIN
Attributes ON AttributeData.AttributeID = Attributes.ID
WHERE (Files.GeneralSearch = 1) AND
(Attributes.Name = 'Process Date' OR Attributes.Name = 'Publish Date') AND
(ISDATE(AttributeData.Keyword) = 1) AND
(CONVERT(DATE, AttributeData.Keyword) > DATEADD(DAY, - 90, GETDATE()))
ORDER BY DateUpdated DESC
DateUpdated DateType
20110318 Process Date
20110318 Process Date
20110315 Process Date
20110315 Process Date
20110303 Process Date
20110303 Publish Date
20110302 Process Date
20110301 Process Date
20110301 Publish Date
20110225 Process Date
20110223 Process Date
20110201 Publish Date
20110201 Process Date
20110127 Process Date
20110118 Publish Date
20110101 Publish Date
20110101 Publish Date
20101231 Process Date
20101231 Publish Date
In SQL Server no particular order of evaluation is guaranteed except for CASE
statements so it may well be doing the CONVERT(DATE, AttributeData.Keyword)
before the ISDATE(AttributeData.Keyword) = 1
filter (you can confirm this by looking at the execution plans).
To get around this you can replace CONVERT(DATE, AttributeData.Keyword)
with
CASE
WHEN ISDATE(AttributeData.Keyword) = 1 THEN
CONVERT(DATE, AttributeData.Keyword)
END
So can you try
SELECT Files.URL,
Files.MD5,
Files.Thumbnail,
Files.Title,
CASE
WHEN ISDATE(AttributeData.Keyword) = 1 THEN
CONVERT(DATE, AttributeData.Keyword)
END AS DateUpdated,
Attributes.Name AS DateType,
Metadata.metadata
FROM Files
INNER JOIN Metadata
ON Files.ID = Metadata.FileID
INNER JOIN FilesToAttributeData
ON Files.ID = FilesToAttributeData.FileID
INNER JOIN AttributeData
ON FilesToAttributeData.AttributeDataID = AttributeData.ID
INNER JOIN Attributes
ON AttributeData.AttributeID = Attributes.ID
WHERE ( Files.GeneralSearch = 1 )
AND ( Attributes.Name = 'Process Date'
OR Attributes.Name = 'Publish Date' )
AND ( CASE
WHEN ISDATE(AttributeData.Keyword) = 1 THEN
CONVERT(DATE, AttributeData.Keyword)
END > DATEADD(DAY, -90, GETDATE()) )
ORDER BY DateUpdated DESC
You could also try adding
(ISDATE(AttributeData.Keyword) = 1)
as a join condition on AttributeData, but I think you are still not guaranteed order of execution there.
And this seems to work fine....
DECLARE @tempFiles TABLE(DateUpdated varchar(MAX))
INSERT INTO @tempFiles(DateUpdated)
SELECT AttributeData.Keyword AS DateUpdated
FROM Files INNER JOIN
Metadata ON Files.ID = Metadata.FileID INNER JOIN
FilesToAttributeData ON Files.ID = FilesToAttributeData.FileID INNER JOIN
AttributeData ON FilesToAttributeData.AttributeDataID = AttributeData.ID INNER JOIN
Attributes ON AttributeData.AttributeID = Attributes.ID
WHERE (Files.GeneralSearch = 1) AND
(Attributes.Name = 'Process Date' OR Attributes.Name = 'Publish Date') AND
(ISDATE(AttributeData.Keyword) = 1) AND
(CONVERT(DATETIME, AttributeData.Keyword, 112) > DATEADD(DAY, - 90, GETDATE()))
ORDER BY DateUpdated DESC
SELECT CONVERT(DATE, DateUpdated) FROM @tempFiles
It appears that Martin was correct in that the SQL optimizer is reordering which parts get evaluated first which means converts are happening before ISDATE so I am actually processing invalid dates. ATM the only way I know to fix this is to use a temp table and evaluate without the convert in the select statement until I am ready to return the results...
精彩评论