Die Konvertierung eines varchar-Datentyps in einen datetime-Datentyp führte zu einem Wert außerhalb des Bereichs

8

Ich versuche, eine einfache Abfrage auszuführen, um alle Zeilen im November zu erstellen:

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

SMSS gibt zurück:

Die Konvertierung eines Varchar-Datentyps in einen Datetime-Datentyp führte zu einem Wert außerhalb des Bereichs.

Ich verstehe nicht, warum die Daten von varchar in datetime konvertiert werden, wenn 'Created' auf datetime eingestellt ist:

Säulen Muss ich dem Server mitteilen, dass "Erstellt" datetime ist? Wenn nicht, warum erhalte ich diese Varchar-Nachricht?

Bearbeiten: Der Wert in der Datenbank war YYYY-MM-DD. Die Antwort von @SqlZim unten besagt, dass ich convert () verwenden muss, um SQL mitzuteilen, welches Format das Datum in der Datenbank hat - und um das Leerzeichen durch den Buchstaben T zu ersetzen:

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
quelle

Antworten:

8

Ich habe Ihr Profil überprüft und festgestellt, dass Sie sich in Großbritannien befinden. Wenn Ihr SQL Server so eingestellt ist, dass er das Datumsformat dmy verwendet, erklärt dies Ihr Problem. Ohne die Verwendung des 'T' anstelle des Leerzeichens in der Datums- / Uhrzeitzeichenfolge erkennt SQL Server es nicht als ISO8601-Format.

Versuche dies:

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');

Das Abfragen mit Datumsangaben und / oder Datumsangaben kann schwierig sein. Um sicherzustellen, dass Sie das bekommen, wonach Sie suchen, empfehle ich Folgendes:

Bearbeiten: Um den Wert außerhalb des Bereichs in Ihrer Fehlermeldung zu verdeutlichen, müssen Sie den Monat als 30 und den Tag als 11 interpretieren.

SqlZim
quelle
8

Ich verstehe nicht, warum die Daten von varchar in datetime konvertiert werden, wenn 'Created' auf datetime eingestellt ist

Die Literale, die Sie zum Vergleich mit der CreatedSpalte bereitstellen, sind Zeichenfolgen. Um diese Literale mit der datetimeSpalte zu vergleichen , versucht SQL Server, die Zeichenfolgen datetimegemäß den Regeln für die Priorität des Datentyps in Typen zu konvertieren . Ohne explizite Informationen zum Format der Zeichenfolgen befolgt SQL Server seine verschlungenen Regeln für die Interpretation von Zeichenfolgen als Datumsangaben.

Meiner Ansicht nach ist der beste Weg, um diese Art von Problemen zu vermeiden, die explizite Angabe von Typen. SQL Server bietet die CAST and CONVERTFunktionen für diesen Zweck. Bei der Arbeit mit Zeichenfolgen und Datums- / Uhrzeittypen CONVERTist dies zu bevorzugen, da es einen Stilparameter zum expliziten Definieren des Zeichenfolgenformats bereitstellt.

Die Frage verwendet Zeichenfolgen im kanonischen ODBC-Format (mit Millisekunden) (Stil 121). Die explizite Angabe des Datentyps und des Zeichenfolgenstils führt zu folgenden Ergebnissen:

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);

Trotzdem gibt es gute Gründe (wie Aaron in seiner Antwort hervorhebt ), stattdessen einen halboffenen Bereich zu verwenden BETWEEN(ich verwende Stil 120 unten nur zur Abwechslung):

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);

Es ist eine sehr gute Angewohnheit, sich explizit mit Typen zu befassen, insbesondere wenn es um Daten und Zeiten geht.

Paul White 9
quelle
3

Eine weitere Alternative ist die Verwendung von ODBC-Datetime-Literalen . Trotz ihres Namens müssen Sie keine Verbindung über ODBC herstellen. Sie umgehen die üblichen Konvertierungsregeln in SQL Server und werden immer als interpretiert 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'};


Die anderen unterstützten ODBC- datetimeLiterale sind Dund Twie hier in Books Online dokumentiert . Beide geben zurück datetime(nicht dateoder time), aber die Syntax ist immer noch kompakt und eindeutig. Die festen Formate für die Zeichenfolgen sind:

ODBC-Zeichenfolgenformate

Beispiel:

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

Die TVariante gibt die am aktuellen Tag angegebene Zeit zurück , wie vom Nur-internen Gebrauch gemeldet {fn getdateODBC()}:

Ausführungsplan

Michael B.
quelle
1
Nun, ich würde es wahrscheinlich nur tun, CONVERT(DATE, '20141201')wenn Ihr Bedürfnis, explizit zu sein, alles andere außer Kraft setzt. Wenn es sich bei der zugrunde liegenden Spalte um einen Datums- / Uhrzeittyp handelt, ist dies nicht unbedingt erforderlich. Wollen Sie WHERE Active = CONVERT(BIT, 1)vermeiden, WHERE Active = 1als interpretiert zu werden INT?
Aaron Bertrand
3
@AaronBertrand Eigentlich war ich dafür bekannt, genau das zu tun :) Und hier ist ein Beispiel dafür, warum .
Paul White 9
-1

Der folgende Code dateformatruft die aktuelle Sitzung ab , zeigt beim Konvertieren in datetime einen Fehler an, setzt dann das Datumsformat auf ymd und testet nicht zuletzt die Konvertierung (Casting) erneut und es funktioniert

-- 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. Oktober 2016 18:17:30 Copyright (c) Microsoft Corporation Enterprise Edition: Kernbasierte Lizenzierung (64-Bit) unter Windows Server 2012 R2 Datacenter 6.3 (Build 9600 :) (Hypervisor)

Marcello Miorelli
quelle
Pflege erklären Sie die Abstimmung nach unten?, der Code funktioniert hier gut!
Marcello Miorelli