Étrange problème lors de la conversion de VarChar (max) en DATE ou DATETIME dans SQL Server 2008

SELECT Files.URL, Files.MD5, Files.Thumbnail, Files.Title, CONVERT(DATE, AtsortingbuteData.Keyword) AS DateUpdated, Atsortingbutes.Name AS DateType, Metadata.metadata FROM Files INNER JOIN Metadata ON Files.ID = Metadata.FileID INNER JOIN FilesToAtsortingbuteData ON Files.ID = FilesToAtsortingbuteData.FileID INNER JOIN AtsortingbuteData ON FilesToAtsortingbuteData.AtsortingbuteDataID = AtsortingbuteData.ID INNER JOIN Atsortingbutes ON AtsortingbuteData.AtsortingbuteID = Atsortingbutes.ID WHERE (Files.GeneralSearch = 1) AND (Atsortingbutes.Name = 'Process Date' OR Atsortingbutes.Name = 'Publish Date') AND (ISDATE(AtsortingbuteData.Keyword) = 1) AND (CONVERT(DATE, AtsortingbuteData.Keyword) > DATEADD(DAY, - 90, GETDATE())) ORDER BY DateUpdated DESC 

C'est ma requête sql d'origine. Il semble fonctionner correctement avec nos données dans notre environnement de développement. En production cependant j'obtiens l'erreur suivante. Msg 241, niveau 16, état 1, ligne 1 La conversion a échoué lors de la conversion de la date et / ou de l'heure de la string de caractères. ". Maintenant, si je supprime la fonction de conversion dans le SELECT et juste la sortie AtsortingbuteData.Keyword, cela fonctionnera bien. Si je laisse cela seul et supprime la fonction de conversion dans la clause where cela fonctionnera bien. Cela ne fonctionnera pas s'ils existent tous les deux.

Des idées qui pourraient causer cela? J'ai essayé CAST et j'ai essayé d'utiliser un style de date spécifique. Nos dates ressemblent généralement à aaaaammjj. Un exemple est '20110318'. Si je remplace AtsortingbuteData.Keyword par cette string, il échouera également. Je n'ai vraiment aucune idée de ce qui se passe ici.

Voici un exemple de requête qui fonctionne.

 SELECT (AtsortingbuteData.Keyword) AS DateUpdated, Atsortingbutes.Name AS DateType FROM Files INNER JOIN Metadata ON Files.ID = Metadata.FileID INNER JOIN FilesToAtsortingbuteData ON Files.ID = FilesToAtsortingbuteData.FileID INNER JOIN AtsortingbuteData ON FilesToAtsortingbuteData.AtsortingbuteDataID = AtsortingbuteData.ID INNER JOIN Atsortingbutes ON AtsortingbuteData.AtsortingbuteID = Atsortingbutes.ID WHERE (Files.GeneralSearch = 1) AND (Atsortingbutes.Name = 'Process Date' OR Atsortingbutes.Name = 'Publish Date') AND (ISDATE(AtsortingbuteData.Keyword) = 1) AND (CONVERT(DATE, AtsortingbuteData.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 

Dans SQL Server, aucun ordre particulier d'évaluation n'est garanti, sauf pour les instructions CASE . Il est donc possible qu'il ISDATE(AtsortingbuteData.Keyword) = 1 CONVERT(DATE, AtsortingbuteData.Keyword) avant le ISDATE(AtsortingbuteData.Keyword) = 1 (vous pouvez le vérifier en examinant l'exécution des plans).

Pour contourner cela, vous pouvez replace CONVERT(DATE, AtsortingbuteData.Keyword) par

  CASE WHEN ISDATE(AtsortingbuteData.Keyword) = 1 THEN CONVERT(DATE, AtsortingbuteData.Keyword) END 

Alors pouvez-vous essayer

 SELECT Files.URL, Files.MD5, Files.Thumbnail, Files.Title, CASE WHEN ISDATE(AtsortingbuteData.Keyword) = 1 THEN CONVERT(DATE, AtsortingbuteData.Keyword) END AS DateUpdated, Atsortingbutes.Name AS DateType, Metadata.metadata FROM Files INNER JOIN Metadata ON Files.ID = Metadata.FileID INNER JOIN FilesToAtsortingbuteData ON Files.ID = FilesToAtsortingbuteData.FileID INNER JOIN AtsortingbuteData ON FilesToAtsortingbuteData.AtsortingbuteDataID = AtsortingbuteData.ID INNER JOIN Atsortingbutes ON AtsortingbuteData.AtsortingbuteID = Atsortingbutes.ID WHERE ( Files.GeneralSearch = 1 ) AND ( Atsortingbutes.Name = 'Process Date' OR Atsortingbutes.Name = 'Publish Date' ) AND ( CASE WHEN ISDATE(AtsortingbuteData.Keyword) = 1 THEN CONVERT(DATE, AtsortingbuteData.Keyword) END > DATEADD(DAY, -90, GETDATE()) ) ORDER BY DateUpdated DESC 

Vous pouvez également essayer d'append

 (ISDATE(AtsortingbuteData.Keyword) = 1) 

comme une condition de jointure sur AtsortingbuteData, mais je pense que vous n'êtes toujours pas garanti l'ordre d'exécution là-bas.

Et cela semble fonctionner correctement ….

  DECLARE @tempFiles TABLE(DateUpdated varchar(MAX)) INSERT INTO @tempFiles(DateUpdated) SELECT AtsortingbuteData.Keyword AS DateUpdated FROM Files INNER JOIN Metadata ON Files.ID = Metadata.FileID INNER JOIN FilesToAtsortingbuteData ON Files.ID = FilesToAtsortingbuteData.FileID INNER JOIN AtsortingbuteData ON FilesToAtsortingbuteData.AtsortingbuteDataID = AtsortingbuteData.ID INNER JOIN Atsortingbutes ON AtsortingbuteData.AtsortingbuteID = Atsortingbutes.ID WHERE (Files.GeneralSearch = 1) AND (Atsortingbutes.Name = 'Process Date' OR Atsortingbutes.Name = 'Publish Date') AND (ISDATE(AtsortingbuteData.Keyword) = 1) AND (CONVERT(DATETIME, AtsortingbuteData.Keyword, 112) > DATEADD(DAY, - 90, GETDATE())) ORDER BY DateUpdated DESC SELECT CONVERT(DATE, DateUpdated) FROM @tempFiles 

Il semble que Martin ait eu raison de dire que l'optimiseur SQL réorganise les parties qui sont évaluées en premier, ce qui signifie que les conversions ont lieu avant ISDATE, donc je suis en train de traiter des dates invalides. ATM la seule façon que je sais pour résoudre ce problème est d'utiliser une table temporaire et d'évaluer sans convertir dans l'instruction select jusqu'à ce que je suis prêt à returnner les résultats …