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

8

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:

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

Modifier: 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');`
jedluddley
la source

Réponses:

8

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.

SqlZim
la source
8

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 Createdcolonne sont des chaînes. Pour comparer ces littéraux à la datetimecolonne, SQL Server tente de convertir les chaînes en datetimetypes, conformément aux règles de priorité des types 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 CONVERTfonctions à cet effet. Lorsque vous travaillez avec des chaînes et des types de date / heure, CONVERTest 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 canonique ODBC (avec millisecondes) (style 121). Ê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 de bonnes raisons (comme le souligne Aaron dans sa réponse ) d'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.

Paul White 9
la source
3

Une autre alternative, je recommande d'utiliser des littéraux datetime ODBC . Malgré leur nom, ils ne nécessitent pas que vous vous connectiez via ODBC. Ils contournent les règles de conversion habituelles dans SQL Server et sont toujours interprétés comme un datetime.

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


Les autres datetimelittéraux ODBC pris en charge sont Det Tcomme indiqué ici dans la documentation en ligne. Les deux retournent datetime(pas dateou time), mais la syntaxe est toujours compacte et sans ambiguïté. Les formats fixes pour les chaînes sont:

Formats de chaîne ODBC

Exemple:

SELECT TOP (1)
    D = {D '2014-12-27'},
    T = {T '14:49:23.789'},
    TS = {TS '2014-12-27 14:49:23.789'};

La Tvariante renvoie l'heure spécifiée le jour en cours , telle que rapportée par l'usage interne uniquement {fn getdateODBC()}:

Plan d'exécution

Michael B
la source
1
Eh bien, je le ferais probablement CONVERT(DATE, '20141201')si votre besoin d'être explicite l'emportait sur tout le reste. Là encore, si la colonne sous-jacente est de type date / heure, ce n'est pas vraiment nécessaire. Dites-vous WHERE Active = CONVERT(BIT, 1)d'éviter WHERE Active = 1d'être interprété comme un INT?
Aaron Bertrand
3
@AaronBertrand En fait, je suis connu pour faire exactement cela :) Et voici un exemple de pourquoi .
Paul White 9
-1

le code ci-dessous, obtenez la session en cours dateformat, obtient une erreur lors de la conversion en datetime, puis redéfinit le format de date ymd et enfin et surtout testez à nouveau la conversion (casting) et cela fonctionne

-- set the dateformat for the current session
-- if you use this date format you get the following error message:
--Msg 242, Level 16, State 3, Line 9
--The conversion of a varchar data type to a datetime data type resulted in an out-of-range value.
set dateformat dmy


-- set the dateformat for the current session
--this one does not give an error message
set dateformat ymd

-- The conversion of a varchar data type 
-- to a datetime data type resulted in an out-of-range value.
select cast('2017-08-13 16:31:31'  as datetime)

-- get the current session date_format
select date_format
from sys.dm_exec_sessions
where session_id = @@spid

-- set the dateformat for the current session
set dateformat ymd

-- this should work
select cast('2017-08-13 16:31:31'  as datetime)



select @@version

Microsoft SQL Server 2016 (SP1) (KB3182545) - 13.0.4001.0 (X64) 28 octobre 2016 18:17:30 Copyright (c) Microsoft Corporation Enterprise Edition: licence basée sur les cœurs (64 bits) sur Windows Server 2012 R2 Datacenter 6.3 (Build 9600:) (Hyperviseur)

Marcello Miorelli
la source
soin d'expliquer le vote négatif?, le code fonctionne bien ici!
Marcello Miorelli