Come combinare data e ora a datetime2 in SQL Server?


48

Dati i seguenti componenti

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

Qual è il modo migliore di combinarli per produrre un DATETIME2(7)risultato con valore '2013-10-13 23:59:59.9999999'?

Alcune cose che non funzionano sono elencate di seguito.


SELECT @D + @T 

La data del tipo di dati dell'operando non è valida per l'operatore add.


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

Il tipo di dati dell'operando datetime2 non è valido per l'operatore add.


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

La funzione dateiff ha provocato un overflow. Il numero di parti della data che separano due istanze di data / ora è troppo grande. Prova a usare datiff con un datepart meno preciso.

* È possibile evitare l'overflow nel database SQL di Azure e in SQL Server 2016 usando DATEDIFF_BIG.


SELECT CAST(@D AS DATETIME) + @T 

I tipi di dati datetime e time non sono compatibili nell'operatore add.


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

Restituisce un risultato ma perde precisione 2013-10-13 23:59:59.997

Risposte:


49

Questo sembra funzionare e mantenere anche la precisione:

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

Il CASTto DATETIME2(7)converte il TIME(7)valore ( @T) in un punto in DATETIME2cui si trova la parte della data '1900-01-01', che è il valore predefinito dei tipi di data e data (vedere datetime2e il commento * su CASTeCONVERT pagina su MSDN.)

* ... Quando i dati di carattere che rappresenta solo la data o componenti di tempo solo è gettato al datetime o smalldatetime tipi di dati, la componente temporale non specificato è impostato su 00: 00: 00.000, e la componente data imprecisata è impostato su 1900-01- 01 .

La funzione DATEADD()e DATEDIFF()si occupa del resto, vale a dire aggiungendo la differenza in giorni tra il 1900-01-01e il DATEvalore ( @D).

Test su: SQL-Fiddle


Come notato da @Quandary , l'espressione sopra è considerata non deterministica da SQL Server. Se vogliamo un'espressione deterministica, supponiamo che debba essere utilizzata per una PERSISTEDcolonna, '19000101'** deve essere sostituito da 0o 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)non è deterministico in quanto comporta una conversione implicita della stringa in DATETIMEe le conversioni da stringhe a datetime sono deterministiche solo quando vengono utilizzati stili specifici.


8

Sono in ritardo alla festa, ma questo approccio, sebbene simile alla risposta di @ ypercube , evita la necessità di utilizzare qualsiasi conversione di stringa (che può essere più costosa delle conversioni di data), è deterministica e dovrebbe continuare a funzionare se gli Stati membri cambiassero valore di data predefinito da 1900-01-01 (anche se probabilmente non lo cambieranno):

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

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

Il principio è che convertendo il valore dell'ora in datetime2 e quindi fino ad oggi, elimina il timeout e assegna la data predefinita, quindi la si annulla con il valore della data per ottenere i giorni da aggiungere, si passa il proprio tempo a datetime2 e si aggiunge giorni dopo.


Invece di "DATEDIFF (DAY, @T, @D)" dovrebbe essere "DATEDIFF (DAY, 0, @D)". Il risultato è lo stesso, ma aiuta a evitare confusione. DateDiff (day, ...) lancia gli argomenti al numero int minimo di giorni, quindi @T viene comunque convertito in 0.
Dennis Gorelik,

5

Per SQL Server 2012 e versioni successive è presente la funzione DATETIME2FROMPARTS . Ha questa forma:

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

Per i dati campione forniti questo diventa

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

che risulta in

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

Le parti possono essere ottenute utilizzando DATEPART () se a partire da tipi di dati temporali o dal testo utilizzato per costruire i valori di esempio nella domanda.


0

È piuttosto stupido da parte di SQL Server non far funzionare il tuo primo esempio, e anche questo sembrerà davvero stupido, ma ...

select convert(datetime2, convert(nvarchar(max), @d) + ' ' + convert(nvarchar(max), @t));

0
SELECT mydate=CAST(CAST(@D AS nvarchar(max)) + ' ' + 
                   CAST(@T AS nvarchar (max)) 
              AS DATETIME2);

5
Hai provato questo? Funziona? È interessato dalle impostazioni della lingua?
ypercubeᵀᴹ

0

Stavo cercando qualcos'altro quando sono atterrato qui. La domanda è piuttosto vecchia, ma ci sono alcuni commenti e attività recenti. Ho pensato di condividere un metodo semplice che è molto simile alla risposta fornita da @Atario, ma un po 'più breve e alcuni potrebbero trovare più facile da leggere:

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

select cast(concat(@d, ' ', @t) as datetime2(7))

-3

È possibile troncare il cast con DATE per troncare, quindi tornare a DATETIME per aggiungere TIME

select CAST( cast(getdate() as date) as DATETIME)  + CAST(getdate() as TIME)

Il trucco è buono, ma non risponde alla domanda in alto.
Peter dice di reintegrare Monica il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.