Wie kombiniere ich Datum und Uhrzeit mit datetime2 in SQL Server?

48

Gegeben die folgenden Komponenten

DECLARE @D DATE = '2013-10-13'
DECLARE @T TIME(7) = '23:59:59.9999999'

Wie lassen sie sich am besten kombinieren, um ein DATETIME2(7)wertvolles Ergebnis zu erzielen '2013-10-13 23:59:59.9999999'?

Einige Dinge, die nicht funktionieren, sind unten aufgeführt.


SELECT @D + @T 

Das Datum des Operandendatentyps ist für den Operator add ungültig.


SELECT CAST(@D AS DATETIME2(7)) + @T 

Der Operandendatentyp datetime2 ist für den Operator add ungültig.


SELECT DATEADD(NANOSECOND,DATEDIFF(NANOSECOND,CAST('00:00:00.0000000' AS TIME),@T),@D)

Die Datediff-Funktion führte zu einem Überlauf. Die Anzahl der Datumsteile, die zwei Datums- / Uhrzeitinstanzen trennen, ist zu groß. Versuchen Sie, Datediff mit einem weniger genauen Datum zu verwenden.

* Der Überlauf kann in Azure SQL Database und SQL Server 2016 mithilfe von vermieden werden DATEDIFF_BIG.


SELECT CAST(@D AS DATETIME) + @T 

Die Datentypen datetime und time sind im Operator add nicht kompatibel.


SELECT CAST(@D AS DATETIME) + CAST(@T AS DATETIME)

Gibt ein Ergebnis zurück, verliert aber an Genauigkeit 2013-10-13 23:59:59.997

Martin Smith
quelle

Antworten:

49

Dies scheint zu funktionieren und auch die Präzision beizubehalten:

SELECT DATEADD(day, DATEDIFF(day,'19000101',@D), CAST(@T AS DATETIME2(7)))

Das CASTto DATETIME2(7)konvertiert den TIME(7)Wert ( @T) in einen DATETIME2Bereich '1900-01-01', in dem sich der Datumsteil befindet. Dies ist der Standardwert für Datums- und Datums- / Uhrzeittypen (siehe datetime2und Kommentar * bei CASTundCONVERT Seite bei MSDN).

* ... Wenn Zeichendaten, die nur Datums- oder Zeitkomponenten darstellen, in den Datentyp datetime oder smalldatetime umgewandelt werden, wird die nicht angegebene Zeitkomponente auf 00: 00: 00.000 und die nicht angegebene Datumskomponente auf 1900-01- gesetzt. 01 .

Die DATEADD()und DATEDIFF()Funktion erledigt den Rest, dh addiert die Differenz in Tagen zwischen dem 1900-01-01und dem DATEWert ( @D).

Test bei: SQL-Fiddle


Wie von @Quandary bemerkt , wird der obige Ausdruck von SQL Server als nicht deterministisch angesehen. Wenn wir einen deterministischen Ausdruck wollen, sagen wir, weil er für eine PERSISTEDSpalte verwendet werden soll, muss das '19000101'** durch 0oder ersetzt werden CONVERT(DATE, '19000101', 112):

CREATE TABLE date_time
( d DATE NOT NULL,
  t TIME(7) NOT NULL,
  dt AS DATEADD(day, 
                DATEDIFF(day, CONVERT(DATE, '19000101', 112), d), 
                CAST(t AS DATETIME2(7))
               ) PERSISTED
) ;

**: DATEDIFF(day, '19000101', d)ist nicht deterministisch, da eine implizite Konvertierung der Zeichenfolge in DATETIMEund Konvertierungen von Zeichenfolgen in datetime nur dann deterministisch sind , wenn bestimmte Stile verwendet werden.

ypercubeᵀᴹ
quelle
8

Ich komme zu spät zur Party, aber dieser Ansatz, der der Antwort von @ ypercube ähnelt , vermeidet die Notwendigkeit, eine Zeichenfolgenkonvertierung zu verwenden (die teurer sein kann als Datumskonvertierungen ), ist deterministisch und sollte weiterhin funktionieren, wenn MS jemals die Änderung vornimmt Standard-Datumswert von 1900-01-01 (obwohl sie dies wahrscheinlich nicht ändern werden):

DECLARE @D DATE = SYSUTCDATETIME()
, @T TIME = SYSUTCDATETIME();

SELECT DATEADD(DAY, DATEDIFF(DAY, @T, @D), CONVERT(DATETIME2, @T));

Das Prinzip besteht darin, dass durch Konvertieren des Zeitwerts in datetime2 und anschließend in date das Zeitlimit entfernt und das Standarddatum zugewiesen wird. Anschließend wird dieses Datum mit Ihrem Datumswert datiert, um die Tage zum Hinzufügen zu erhalten, die Zeit in datetime2 zu übertragen und das Datum hinzuzufügen Tage später.

Knöchel
quelle
Anstelle von "DATEDIFF (DAY, @T, @D)" sollte es "DATEDIFF (DAY, 0, @D)" sein. Das Ergebnis ist das gleiche, hilft aber, Verwirrung zu vermeiden. DateDiff (day, ...) wandelt Argumente in die niedrigste Ganzzahl von Tagen um, sodass @T ohnehin in 0 konvertiert wird.
Dennis Gorelik
5

Für SQL Server 2012 und höher gibt es die Funktion DATETIME2FROMPARTS . Es hat diese Form:

DATETIME2FROMPARTS(year, month, day, hour, minute, seconds, fractions, precision)

Für die angegebenen Beispieldaten wird dies

select Answer = DATETIME2FROMPARTS(2013, 10, 13, 23, 59, 59, 9999999, 7);

was in ... endet

Answer
---------------------------
2013-10-13 23:59:59.9999999

Die Teile können mit DATEPART () abgerufen werden, wenn Sie von temporären Datentypen oder von dem Text ausgehen, der zum Erstellen der Beispielwerte in der Frage verwendet wird.

Michael Green
quelle
0

Es ist ziemlich dumm von SQL Server, Ihr erstes Beispiel nicht funktionieren zu lassen, und das wird auch wirklich dumm erscheinen, aber ...

select convert(datetime2, convert(nvarchar(max), @d) + ' ' + convert(nvarchar(max), @t));
Atario
quelle
0
SELECT mydate=CAST(CAST(@D AS nvarchar(max)) + ' ' + 
                   CAST(@T AS nvarchar (max)) 
              AS DATETIME2);
Mihir
quelle
5
Hast du das getestet? Funktioniert es? Wird es von den Spracheinstellungen beeinflusst?
ypercubeᵀᴹ
0

Ich habe nach etwas anderem gesucht, als ich hier gelandet bin. Die Frage ist ziemlich alt, aber es gibt einige aktuelle Kommentare und Aktivitäten. Ich dachte, ich würde eine einfache Methode teilen, die der Antwort von @Atario sehr ähnlich ist, aber ein bisschen kürzer und einige könnten leichter zu lesen sein:

declare @d date = '2013-10-13'
declare @t time(7) = '23:59:59.9999999'

select cast(concat(@d, ' ', @t) as datetime2(7))
Brian Jorden
quelle
-3

Sie können das mit Cast auf DATE kürzen, um es zu kürzen, und dann zurück auf DATETIME, um die TIME hinzuzufügen

select CAST( cast(getdate() as date) as DATETIME)  + CAST(getdate() as TIME)
user3448451
quelle
Der Trick ist gut, aber er beantwortet die Frage oben nicht.
user259412