Da Sie datetimeDatentypen verwenden, müssen Sie verstehen, wie SQL Server Datum / Uhrzeit-Daten rundet.
╔═══════════╦═════╦═════════════════════════════╦═════════════════════════════╦══════════╦═══════════╗
║ Name ║ sn ║ Minimum value ║ Maximum value ║ Accuracy ║ Storage ║
╠═══════════╬═════╬═════════════════════════════╬═════════════════════════════╬══════════╬═══════════╣
║ datetime ║ dt ║ 1753-01-01 00:00:00.000 ║ 9999-12-31 23:59:59.997 ║ 3.33 ms ║ 8 bytes ║
║ datetime2 ║ dt2 ║ 0001-01-01 00:00:00.0000000 ║ 9999-12-31 23:59:59.9999999 ║ 100ns ║ 6-8 bytes ║
╚═══════════╩═════╩═════════════════════════════╩═════════════════════════════╩══════════╩═══════════╝
Wenn Sie die folgende Abfrage verwenden, können Sie leicht das Problem der Rundung erkennen, das SQL Server bei Verwendung des DATETIME
Datentyps ausführt .
select '2015-07-27 00:00:00.000' as Original_startDateTime,
convert(datetime ,'2015-07-27 00:00:00.000') as startDateTime,
'2015-07-27 23:59:59.999' as Original_endDateTime,
convert(datetime ,'2015-07-27 23:59:59.999') as endDateTime,
'2015-07-27 00:00:00.000' as Original_startDateTime2,
convert(datetime2 ,'2015-07-27 00:00:00.000') as startDateTime2, -- default precision is 7
'2015-07-27 23:59:59.999' as Original_endDateTime2,
convert(datetime2 ,'2015-07-27 23:59:59.999') as endDateTime2 -- default precision is 7
klicken um zu vergrößern
DATETIME2
gibt es seit SQL Server 2008, verwenden Sie ihn also anstelle von DATETIME
. Für Ihre Situation können Sie datetime2
mit einer Genauigkeit von 3 Dezimalstellen verwenden, z datetime2(3)
.
Vorteile der Verwendung von datetime2
:
- Unterstützt bis zu 7 Dezimalstellen für Zeitkomponente vs
datetime
nur 3 Dezimalstellen unterstützt .. und daher sieht man die Rundung Problem , da standardmäßig datetime
Runden nächsten .003 seconds
mit Schritten .000
, .003
oder .007
Sekunden.
datetime2
ist viel genauer als datetime
und datetime2
gibt Ihnen die Kontrolle über DATE
und TIME
im Gegensatz zu datetime
.
Referenz :
gives you control of DATE and TIME as opposed to datetime.
was bedeutet das?DateTime2
vsDateTime
.: a. Für die überwiegende Mehrheit der Anwendungsfälle in der realen Welt sind die Vorteile vonDateTime2
Much <cost. Siehe: stackoverflow.com/questions/1334143/… b. Das ist nicht die Wurzel Problem hier. Siehe nächsten Kommentar.datetime3
70 (gegenüber 7) Stellen Genauigkeit hinzugefügt werden?). Es wird empfohlen, einen Wert zu verwenden, bei dem die Genauigkeit keine Rolle spielt, dh <der Beginn der nächsten Sekunde, Minute, Stunde oder Tag vs. <= das Ende der vorherigen Sekunde, Minute, Stunde oder Tag.Wie mehrere andere in Kommentaren und anderen Antworten auf Ihre Frage erwähnt haben, wird das Kernproblem von SQL Server
2015-07-27 23:59:59.999
abgerundet2015-07-28 00:00:00.000
. Gemäß der Dokumentation für DATETIME:Beachten Sie, dass der Zeitbereich niemals sein kann
.999
. Weiter unten in der Dokumentation werden die Rundungsregeln angegeben, die SQL Server für die niedrigstwertige Ziffer verwendet.Beachten Sie, dass die niedrigstwertige Ziffer nur einen von drei möglichen Werten haben kann: "0", "3" oder "7".
Hierfür gibt es mehrere Lösungen / Problemumgehungen, die Sie verwenden können.
Von den fünf Optionen, die ich oben vorgestellt habe, halte ich die Optionen 1 und 3 für die einzig praktikablen Optionen. Sie vermitteln Ihre Absichten klar und brechen nicht zusammen, wenn Sie Datentypen aktualisieren. Wenn Sie SQL Server 2008 oder eine neuere Version verwenden, ist Option 3 Ihrer Meinung nach der bevorzugte Ansatz. Dies gilt insbesondere dann, wenn Sie den DATETIMEDatentyp nicht mehr in einen DATEDatentyp für Ihre
posted_date
Spalte ändern können .In Bezug auf Option 3 finden Sie hier eine sehr gute Erklärung zu einigen Problemen: Bisherige Besetzung ist kostbar, aber ist es eine gute Idee?
Ich mag die Optionen 2 und 5 nicht, weil die
.997
Sekundenbruchteile nur eine weitere magische Zahl sein werden , die die Leute "reparieren" wollen. Aus einigen weiteren Gründen, warumBETWEEN
es nicht sehr beliebt ist, möchten Sie vielleicht diesen Beitrag lesen .Ich mag Option 4 nicht, weil das Konvertieren von Datentypen in einen String zu Vergleichszwecken für mich schmutzig ist. Ein qualitativerer Grund, dies in SQL Server zu vermeiden, ist die Beeinträchtigung der Zuverlässigkeit, da Sie keine Indexsuche durchführen können und dies häufig zu einer schlechteren Leistung führt.
Weitere Informationen zum richtigen und falschen Umgang mit Datumsbereichsabfragen finden Sie in diesem Beitrag von Aaron Bertrand .
Während des Abschieds können Sie Ihre ursprüngliche Abfrage beibehalten und sie verhält sich wie gewünscht, wenn Sie Ihre
posted_date
Spalte von a DATETIMEin a ändernDATETIME2(3)
. Das spart Speicherplatz auf dem Server, erhöht die Genauigkeit bei gleicher Genauigkeit, ist standardkonformer / portabler und ermöglicht eine einfache Anpassung der Genauigkeit / Präzision, wenn sich Ihre Anforderungen in Zukunft ändern. Dies ist jedoch nur eine Option, wenn Sie SQL Server 2008 oder höher verwenden.Als eine Kleinigkeit scheint die
1/300
Genauigkeit einer Sekunde mit dieser StackOverflow-Antwort ein NachteilDATETIME von UNIX zu sein . Sybase, das ein gemeinsames Erbe hat, hat eine ähnliche zweite Genauigkeit in ihren und Datentypen, aber ihre am wenigsten signifikanten Ziffern unterscheiden sich bei "0", "3" und "6". Meiner Meinung nach ist die Genauigkeit von einer Sekunde und / oder 3,33 ms eine unglückliche architektonische Entscheidung, da der 4-Byte-Block für die Zeit im SQL Server- Datentyp leicht die Genauigkeit von 1 ms hätte unterstützen können.1/300
DATETIME
TIME
1/300
DATETIMEquelle
datetime3
70 (gegenüber 7) Stellen Genauigkeit hinzugefügt werden? Es wird empfohlen, einen Wert zu verwenden, bei dem die Genauigkeit keine Rolle spielt, dh <der Beginn der nächsten Sekunde, Minute, Stunde oder Tag vs. <= das Ende der vorherigen Sekunde, Minute, Stunde oder Tag.Ich nahm an, dass posted_date Datentyp Datetime ist. Es spielt jedoch keine Rolle, ob der Typ auf der anderen Seite Datetime, Datetime2 oder nur Time ist, da der String (Varchar) implizit in Datetime konvertiert wird.
Wenn posted_date als Datetime2 (oder Time)
posted_date <= '2015-07-27 23:59:59.99999'
deklariert23:59:59.99999
ist , schlägt die where-Klausel fehl, da zwar ein gültiger Datetime2-Wert vorliegt, dies jedoch kein gültiger Datetime-Wert ist:Der Zeitbereich von Datetime reicht von 00:00:00 bis 23: 59: 59.997. Daher ist 23: 59: 59.999 außerhalb des Bereichs und muss auf den nächsten Wert auf- oder abgerundet werden.
Außerdem werden die Datenzeitwerte in Schritten von .000, .003 oder .007 Sekunden gerundet. (dh 000, 003, 007, 010, 013, 017, 020, ..., 997)
Dies ist bei den Werten,
2015-07-27 23:59:59.999
die in diesem Bereich liegen, nicht der Fall :2015-07-27 23:59:59.997
und2015-07-28 0:00:00.000
.Dieser Bereich entspricht den nächsthöheren vorhergehenden und folgenden Optionen, die beide mit .000, .003 oder .007 enden.
Da es sich näher an
2015-07-28 0:00:00.000
(+1 gegenüber -2) als2015-07-27 23:59:59.997
, wird die Zeichenfolge aufgerundet und wird diesen Wert für Datum und Uhrzeit:2015-07-28 0:00:00.000
.Mit einer Obergrenze wie
2015-07-27 23:59:59.998
(oder .995, .996, .997, .998) wäre es abgerundet worden2015-07-27 23:59:59.997
und Ihre Abfrage hätte wie erwartet funktioniert. Es wäre jedoch keine Lösung gewesen, sondern nur ein glücklicher Wert.Datetime2 und Zeitzeitbereiche sind
00:00:00.0000000
durch23:59:59.9999999
mit einer Genauigkeit von 100 ns (die letzte Ziffer , wenn sie mit einem 7 - stelligen Genauigkeit verwendet wird ).Ein Datetime-Bereich (3) ist jedoch nicht mit dem Datetime-Bereich vergleichbar:
0:0:00.000
bis23:59:59.997
0:0:00.000000000
bis23:59:59.999
Am Ende ist es sicherer, nach Daten unterhalb des nächsten Tages zu suchen, als nach Daten unterhalb oder gleich dem, was Sie für das letzte Fragment der Tageszeit halten. Dies liegt hauptsächlich daran, dass Sie wissen, dass der nächste Tag immer um 0: 00: 00.000 beginnt, verschiedene Datentypen jedoch möglicherweise nicht dieselbe Uhrzeit am Ende des Tages haben:
< 2015-07-28 0:00:00.000
gibt Ihnen genaue Ergebnisse und ist die beste Option<= 2015-07-27 23:59:59.xxx
kann unerwartete Werte zurückgeben, wenn es nicht auf das aufgerundet wird, was Sie denken, dass es sein sollte.Wir könnten denken, dass das Ändern von [posted_date] in Datetime2 und dessen höhere Genauigkeit dieses Problem beheben könnte, aber es hilft nicht, da die Zeichenfolge immer noch in Datetime konvertiert wird. Wenn jedoch eine Besetzung hinzugefügt wird
cast(2015-07-27 23:59:59.999' as datetime2)
, funktioniert dies einwandfreiCast kann einen Wert mit bis zu 3 Stellen in Datum / Uhrzeit oder mit bis zu 9 Stellen in Datum / Uhrzeit2 oder Uhrzeit konvertieren und auf die richtige Genauigkeit runden.
Es ist zu beachten, dass Cast von Datetime2 und Time2 unterschiedliche Ergebnisse liefern können:
select cast('20150101 23:59:59.999999999' as datetime2(7))
ist rund 2015-05-03 00: 00: 00.0000000 (für einen Wert größer als 999999949)select cast('23:59:59.999999999' as time(7))
=> 23: 59: 59.9999999Es behebt das Problem, das datetime mit den Schritten 0, 3 und 7 hat, obwohl es immer besser ist, nach Daten vor der ersten Nanosekunde des nächsten Tages zu suchen (immer 0: 00: 00.000).
Quell-MSDN: datetime (Transact-SQL)
quelle
Es rundet
.998, .997, .996, .995 alle gegossen / rund bis .997
Sollte nutzen
oder
Siehe Genauigkeit in diesem Link.
Immer gemeldet als .000, .003, .007
quelle
quelle
'DATE' is not a recognized built-in function name.