Como combinar data e hora com datetime2 no SQL Server?

48

Dados os seguintes componentes

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

Qual é a melhor maneira de combiná-los para produzir um DATETIME2(7)resultado com valor '2013-10-13 23:59:59.9999999'?

Algumas coisas que não funcionam estão listadas abaixo.


SELECT @D + @T 

A data do tipo de dados do operando é inválida para o operador add.


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

O tipo de dados do operando datetime2 é inválido para o operador add.


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

A função datatediff resultou em um estouro. O número de datas que separam duas instâncias de data / hora é muito grande. Tente usar datediff com um período menos preciso.

* O estouro pode ser evitado no Banco de Dados SQL do Azure e no SQL Server 2016, usando DATEDIFF_BIG.


SELECT CAST(@D AS DATETIME) + @T 

Os tipos de dados datetime e time são incompatíveis no operador add.


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

Retorna um resultado, mas perde precisão 2013-10-13 23:59:59.997

Martin Smith
fonte

Respostas:

49

Isso parece funcionar e mantém a precisão também:

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

O CASTto DATETIME2(7)converte o TIME(7)valor ( @T) em DATETIME2onde está a parte da data '1900-01-01', que é o valor padrão dos tipos de data e data / hora (consulte datetime2e o comentário * em CASTeCONVERT página no MSDN.)

* ... Quando dados de caracteres que representam apenas componentes de data ou hora são convertidos para os tipos de dados datetime ou smalldatetime, o componente de tempo não especificado é definido como 00: 00: 00.000 e o componente de data não especificado é definido como 1900-01- 01 .

A função DATEADD()e DATEDIFF()cuida do resto, ou seja, adicionando a diferença de dias entre o 1900-01-01e o DATEvalor ( @D).

Teste em: SQL-Fiddle


Conforme observado pelo @Quandary , a expressão acima é considerada não determinística pelo SQL Server. Se queremos uma expressão determinística, digamos, porque ela deve ser usada para uma PERSISTEDcoluna, o '19000101'** precisa ser substituído por 0ou 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)não é determinístico, pois faz uma conversão implícita da sequência de caracteres DATETIMEe as conversões de sequências de caracteres para data e hora são determinísticas apenas quando estilos específicos são usados.

ypercubeᵀᴹ
fonte
8

Estou atrasado para a festa, mas essa abordagem, embora semelhante à resposta do @ ypercube , evita a necessidade de usar qualquer conversão de string (que pode ser mais cara do que as conversões de data), é determinística e deve continuar a funcionar se a MS alterar o valor padrão da data de 1900-01-01 (mesmo que provavelmente não mude isso):

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

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

O princípio é que, ao converter o valor de tempo em datetime2 e, em seguida, até a data, ele retira o tempo limite e atribui a data padrão, e você o diferencia com seu valor de data para obter os dias a serem adicionados, converter seu tempo em datetime2 e adicionar o dias depois.

juntas
fonte
Em vez de "DATEDIFF (DAY, @T, @D)", deve ser "DATEDIFF (DAY, 0, @D)". O resultado é o mesmo, mas ajuda a evitar confusões. DateDiff (day, ...) lança argumentos para o menor número int de dias, então @T é convertido em 0 de qualquer maneira.
Dennis Gorelik
5

Para o SQL Server 2012 e superior, há a função DATETIME2FROMPARTS . Tem esta forma:

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

Para os dados de amostra fornecidos, isso se torna

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

o que resulta em

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

As partes podem ser obtidas usando DATEPART () se iniciando com tipos de dados temporais ou com o texto usado para construir os valores de amostra na pergunta.

Michael Green
fonte
0

É muito estúpido do SQL Server não deixar seu primeiro exemplo funcionar, e isso também vai parecer muito idiota, mas…

select convert(datetime2, convert(nvarchar(max), @d) + ' ' + convert(nvarchar(max), @t));
Atario
fonte
0
SELECT mydate=CAST(CAST(@D AS nvarchar(max)) + ' ' + 
                   CAST(@T AS nvarchar (max)) 
              AS DATETIME2);
Mihir
fonte
5
Você já testou isso? Funciona? É afetado pelas configurações de idioma?
precisa saber é o seguinte
0

Eu estava procurando por outra coisa quando cheguei aqui. A questão é bastante antiga, mas há alguns comentários e atividades recentes. Pensei em compartilhar um método simples que é muito semelhante à resposta que @Atario deu, mas um pouco mais curto e alguns podem achar mais fácil de ler:

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

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

Você pode truncar a conversão com para DATE para truncar e voltar para DATETIME para adicionar o TIME

select CAST( cast(getdate() as date) as DATETIME)  + CAST(getdate() as TIME)
user3448451
fonte
O truque é bom, mas não responde à pergunta no topo.
user259412