¿Cómo combinar fecha y hora con datetime2 en SQL Server?

48

Dados los siguientes componentes

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

¿Cuál es la mejor manera de combinarlos para producir un DATETIME2(7)resultado con valor '2013-10-13 23:59:59.9999999'?

Algunas cosas que no funcionan se enumeran a continuación.


SELECT @D + @T 

La fecha del tipo de datos del operando no es válida para agregar operador.


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

El tipo de datos de operando datetime2 no es válido para agregar operador.


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

La función dateiff resultó en un desbordamiento. El número de partes de fecha que separan dos instancias de fecha / hora es demasiado grande. Intente usar dateiff con una parte de fecha menos precisa.

* El desbordamiento se puede evitar en Azure SQL Database y SQL Server 2016, utilizando DATEDIFF_BIG.


SELECT CAST(@D AS DATETIME) + @T 

Los tipos de datos datetime y time son incompatibles en el operador add.


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

Devuelve un resultado pero pierde precisión 2013-10-13 23:59:59.997

Martin Smith
fuente

Respuestas:

49

Esto parece funcionar y mantener la precisión también:

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

El CASTque DATETIME2(7)convierte el TIME(7)valor de ( @T) a una DATETIME2en la parte de fecha es '1900-01-01', que es el valor por defecto de la fecha y de fecha y hora tipos (ver datetime2y el comentario * a CASTyCONVERT la página en MSDN.)

* ... Cuando los datos de caracteres que representan solo componentes de fecha o solo hora se convierten en los tipos de datos datetime o smalldatetime, el componente de tiempo no especificado se establece en 00: 00: 00.000, y el componente de fecha no especificado se establece en 1900-01- 01 .

La función DATEADD()y DATEDIFF()se encarga del resto, es decir, agregar la diferencia en días entre 1900-01-01el DATEvalor y ( @D).

Prueba en: SQL-Fiddle


Como notó @Quandary , SQL Server considera que la expresión anterior no es determinista. Si queremos una expresión determinista, digamos porque se va a usar para una PERSISTEDcolumna, el '19000101'** necesita ser reemplazado por 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)no es determinista, ya que realiza una conversión implícita de la cadena DATETIMEy las conversiones de cadenas a fecha y hora son deterministas solo cuando se utilizan estilos específicos.

ypercubeᵀᴹ
fuente
8

Llego tarde a la fiesta, pero este enfoque, si bien es similar a la respuesta de @ ypercube , evita la necesidad de usar cualquier conversión de cadena (que puede ser más costosa que las conversiones de fecha), es determinista y debería continuar funcionando si MS alguna vez cambia la valor de fecha predeterminado desde 1900-01-01 (aunque probablemente no cambiarán esto):

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

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

El principio es que al convertir el valor de tiempo a datetime2 y luego a fecha, elimina el tiempo de espera y asigna la fecha predeterminada, luego fecha esto con su valor de fecha para obtener los días para agregar, emitir su tiempo a datetime2 y agregar el días en

nudillos
fuente
En lugar de "DATEDIFF (DAY, @T, @D)" debería ser "DATEDIFF (DAY, 0, @D)". El resultado es el mismo, pero ayuda a evitar confusiones. DateDiff (día, ...) arroja argumentos al número int más bajo de días, por lo que @T se convierte a 0 de todos modos.
Dennis Gorelik
5

Para SQL Server 2012 y superior, existe la función DATETIME2FROMPARTS . Tiene esta forma:

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

Para los datos de muestra dados esto se convierte en

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

lo que resulta en

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

Las partes se pueden obtener utilizando DATEPART () si se parte de tipos de datos temporales o del texto utilizado para construir los valores de muestra en la pregunta.

Michael Green
fuente
0

Es bastante estúpido que SQL Server no permita que su primer ejemplo funcione, y esto también parecerá realmente tonto, pero ...

select convert(datetime2, convert(nvarchar(max), @d) + ' ' + convert(nvarchar(max), @t));
Atario
fuente
0
SELECT mydate=CAST(CAST(@D AS nvarchar(max)) + ' ' + 
                   CAST(@T AS nvarchar (max)) 
              AS DATETIME2);
Mihir
fuente
55
¿Has probado esto? ¿Funciona? ¿Está afectado por la configuración del idioma?
ypercubeᵀᴹ
0

Estaba buscando algo más cuando aterricé aquí. La pregunta es bastante antigua, pero hay algunos comentarios y actividades recientes. Pensé que compartiría un método simple que es muy similar a la respuesta que dio @Atario, pero un poco más corto y algunos podrían ser más fáciles de leer:

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

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

Puede truncar con Cast a DATE para truncar, luego volver a DATETIME para agregar la HORA

select CAST( cast(getdate() as date) as DATETIME)  + CAST(getdate() as TIME)
usuario3448451
fuente
El truco es bueno, pero no responde la pregunta en la parte superior.
user259412