web-dev-qa-db-fra.com

La conversion d'un type de données varchar en un type de données datetime a entraîné une valeur hors plage

J'essaie d'exécuter une requête simple pour obtenir toutes les lignes créées en novembre:

SELECT COUNT(*)
FROM dbo.profile 
WHERE [Created] BETWEEN '2014-11-01 00:00:00.000' 
AND '2014-11-30 23:59:59.997';

SMSS renvoie:

La conversion d'un type de données varchar en un type de données datetime a entraîné une valeur hors plage.

Je ne comprends pas pourquoi les données sont converties de varchar en datetime lorsque 'Created' est défini sur datetime:

Columns Dois-je dire au serveur que "Créé" est datetime? Sinon, pourquoi reçois-je ce message varchar?

Edit: La valeur dans la base de données était YYYY-MM-DD. La réponse de @SqlZim ci-dessous indique que je dois utiliser convert () pour indiquer à sql le format de la date dans la base de données - et pour remplacer le caractère espace par la lettre T:

select count(*) 
from dbo.profile 
where [created] between convert(datetime,'2014-11-01T00:00:00.000') 
and convert(datetime,'2014-11-30T23:59:59.997');`
8
jedluddley

J'ai vérifié votre profil et j'ai vu que vous étiez au Royaume-Uni. Si votre serveur SQL est configuré pour utiliser le format de date dmy, cela explique votre problème. Sans utiliser le "T" au lieu de l'espace dans la chaîne datetime, Sql Server ne le reconnaîtra pas au format ISO8601.

Essaye ça:

select count(*) 
  from dbo.profile 
  where [created] between convert(datetime,'2014-11-01T00:00:00.000') 
                      and convert(datetime,'2014-11-30T23:59:59.997');

L'interrogation à l'aide de dates et/ou d'heures peut être délicate, pour vous assurer d'obtenir ce que vous recherchez, je recommande la lecture:

modifier: pour clarifier la valeur hors plage dans votre message d'erreur, il faudrait interpréter le mois comme 30 et le jour comme 11.

8
SqlZim

Je ne comprends pas pourquoi les données sont converties de varchar en datetime lorsque 'Created' est défini sur datetime

Les littéraux que vous fournissez pour la comparaison avec la colonne Created sont des chaînes. Pour comparer ces littéraux avec la colonne datetime, SQL Server tente de convertir les chaînes en types datetime, selon les règles de priorité du type de données . Sans informations explicites sur le format des chaînes, SQL Server suit ses règles compliquées pour interpréter les chaînes comme des heures.

À mon avis, la meilleure façon d'éviter ces types de problèmes est d'être explicite sur les types. SQL Server fournit les CAST and CONVERT fonctions à cet effet. Lorsque vous travaillez avec des chaînes et des types de date/heure, CONVERT est préférable car il fournit un paramètre de style pour définir explicitement le format de chaîne.

La question utilise des chaînes au format ODBC canonique (avec millisecondes) (style 121). Le fait d'être explicite sur le type de données et le style de chaîne se traduit par ce qui suit:

SELECT COUNT(*)
FROM dbo.profile 
WHERE [Created] BETWEEN 
    CONVERT(datetime, '2014-11-01 00:00:00.000', 121)
    AND 
    CONVERT(datetime, '2014-11-30 23:59:59.997', 121);

Cela dit, il y a bonnes raisons (comme Aaron le souligne dans sa réponse ) pour utiliser une plage semi-ouverte au lieu de BETWEEN (j'utilise le style 120 ci-dessous juste pour la variété):

SELECT COUNT(*)
FROM dbo.profile 
WHERE
    [Created] >= CONVERT(datetime, '2014-11-01 00:00:00', 120)
    AND [Created] < CONVERT(datetime, '2014-12-01 00:00:00', 120);

Être explicite sur les types est une très bonne habitude à prendre, en particulier lorsqu'il s'agit de dates et d'heures.

8
Paul White 9

Étant donné que BETWEEN est très problématique en raison de l'arrondissement de différents types de date/heure et d'autres problèmes , et puisque YYYY-MM-DD n'est pas un format sûr sans le maladroit T , une plage ouverte utilisant la norme ISO les dates complètes sans séparateurs est une bien meilleure approche:

WHERE Created >= '20141101' AND Created < '20141201';
7
Aaron Bertrand