DateTime2 vs DateTime no SQL Server

762

Qual:

é a maneira recomendada de armazenar data e hora no SQL Server 2008+?

Estou ciente das diferenças de precisão (e provavelmente do espaço de armazenamento), mas ignorando-as por enquanto, existe um documento de práticas recomendadas sobre quando usar o que ou talvez devamos usar datetime2apenas apenas?

Mikeon
fonte

Respostas:

641

A documentação do MSDN para datetime recomenda o uso de datetime2 . Aqui está a recomendação deles:

Use as time, date, datetime2e datetimeoffsettipos de dados para o novo trabalho. Esses tipos estão alinhados com o padrão SQL. Eles são mais portáteis. time, datetime2E datetimeoffset fornecer mais segundos precisão. datetimeoffsetfornece suporte de fuso horário para aplicativos implantados globalmente.

datetime2 tem um período maior, uma precisão fracionária padrão maior e precisão especificada pelo usuário opcional. Além disso, dependendo da precisão especificada pelo usuário, ele pode usar menos armazenamento.

Adam Porad
fonte
46
embora exista maior precisão com datetime2, alguns clientes não suportam data, hora ou datetime2 e forçam você a converter em um literal de cadeia de caracteres. Se você está mais preocupado com a compatibilidade do que precisão, use datetime
FistOfFury
4
Outra opção é usar uma exibição indexada com a coluna convertida como data e hora para compatibilidade. Você precisaria apontar o aplicativo para a visualização, no entanto.
TamusJRoyce 8/17/17
9
O suporte ao fuso horário com DATETIMEOFFSET é um nome impróprio. Ele armazena apenas um deslocamento UTC por um instante específico no tempo, não um fuso horário.
precisa saber é o seguinte
6
@Porad: Qual é exatamente o benefício na prática de ser "" mais portátil "devido a ser" SQL Standard "? Isso além de fazer com que você escreva significativamente mais código que seja significativamente menos legível / sustentável para uma" porta "para outro RDBMS que É provável que isso nunca ocorra durante a vida útil desse código.Para além das ferramentas e drivers do SQL Server fornecidos pela Microsoft (se houver), existem aplicativos que realmente dependam das representações específicas em nível de bit do DateTime2tipo (ou qualquer outro SQL Server Digite para que o assunto) Ver os Contras na minha 7/10/17 Resposta abaixo por isso que eu estou pedindo?.
Tom
2
@ Adam Porad: Além disso, todos esses benefícios provavelmente não são necessários (fora de aplicativos de engenharia ou científicos) e, portanto, não valem muito a perda de benefícios, MUITO mais provavelmente necessário: a capacidade muito mais fácil (mesmo considerando soluções alternativas) de converter implicitamente / explicitamente em um valor numérico de ponto flutuante (número de dias, inclusive se aplicável, dias fracionários desde a data e hora mínimas) para adições, subtrações, mínimos, máximos e médias. Veja os contras na minha resposta em 7/10/17 abaixo para obter detalhes.
Tom
493

DATETIME2tem um intervalo de datas de "0001/01/01" a " DATETIME9999/12/31 ", enquanto o tipo suporta apenas o ano 1753-9999.

Além disso, se você precisar, DATETIME2pode ser mais preciso em termos de tempo; DATETIME é limitado a 3 1/3 milissegundos, enquantoDATETIME2 pode ser preciso até 100ns.

Ambos os tipos são mapeados System.DateTimeno .NET - não há diferença.

Se você tiver a opção, eu recomendaria o uso DATETIME2sempre que possível. Não vejo nenhum benefício ao usarDATETIME (exceto a compatibilidade com versões anteriores) - você terá menos problemas (com datas fora do intervalo e sem problemas).

Além disso: se você precisar apenas da data (sem parte da hora), use DATE - é tão bom quanto DATETIME2e economiza espaço também! :-) O mesmo vale apenas para o tempo - use TIME. É para isso que esses tipos existem!

marc_s
fonte
158
Tenha cuidado ao adicionar um valor .NET DateTime como parâmetro a um SqlCommand, porque ele gosta de assumir que é o tipo datetime antigo e você receberá um erro se tentar escrever um valor DateTime que esteja fora desse período de 1753 a 9999 a menos que você especifique explicitamente o tipo como System.Data.SqlDbType.DateTime2 para o SqlParameter. De qualquer forma, datetime2 é ótimo, porque pode armazenar qualquer valor que possa ser armazenado no tipo .NET DateTime.
Triynko 26/10/10
10
@marc_s - Não é para isso que nulo?
precisa saber é
8
@ JohnFX - um pouco tarde aqui - mas você não definiria um datetime para null. você usaria Nullable <datetime> ou datetime? que lida com nulo muito bem - e no mapeamento para um proc simplesmente faria param.value = someDateTime ?? DBValue.Null Sua infeliz que está preso com um tipo de dados com um número após ele - parece tão 'genérico':)
Adam Tuliper - MSFT
69
Lol, eu apenas tentei aprovar meu próprio comentário (acima), antes de perceber que era meu próprio comentário (feito há mais de um ano). Ainda estou lidando com a decisão de design burro do .NET framework para TRUNCATE todos os valores de DateTime por padrão quando passados ​​como SqlParameters, a menos que você o defina explicitamente como SqlDbType.DateTime2 mais preciso. Tanta coisa para inferir automaticamente o tipo correto. Realmente, eles deveriam ter tornado a alteração transparente, substituindo a implementação menos precisa, menos eficiente e de alcance limitado, e mantido o nome original do tipo "datetime". Veja também stackoverflow.com/q/8421332/88409
Triynko
5
@marc_s Não Nullable<DateTime>é para isso que serve?
ChrisW
207

datetime2 vence na maioria dos aspectos, exceto (aplicativos antigos Compatibilidade)

  1. maior faixa de valores
  2. melhor precisão
  3. espaço de armazenamento menor (se precisão especificada pelo usuário opcional for especificada)

Compare os tipos de dados de data e hora do SQL - datetime, datetime2, date, TIME

observe os seguintes pontos

  • Sintaxe
    • datetime2 [(segundos fracionários de precisão => Olhar Abaixo do Tamanho do Armazenamento)]
  • Precisão, escala
    • 0 a 7 dígitos, com precisão de 100ns.
    • A precisão padrão é de 7 dígitos.
  • Tamanho de armazenamento
    • 6 bytes para precisão menor que 3;
    • 7 bytes para a precisão 3 e 4.
    • Todas as outras precisão requerem 8 bytes .
  • DateTime2 (3) tem o mesmo número de dígitos que DateTime, mas usa 7 bytes de armazenamento em vez de 8 bytes ( SQLHINTS- DateTime vs. DateTime2 )
  • Saiba mais sobre datetime2 (artigo Transact-SQL MSDN)

fonte da imagem: Kit de treinamento individual do MCTS (Exame 70-432): Microsoft® SQL Server® 2008 - Implementação e manutenção Capítulo 3: Tabelas -> Lição 1: Criando tabelas -> página 66

Iman
fonte
7
Obrigado por mostrar que as estatísticas +1 para isso, datetime2é incrível (Winner)
Pankaj Parkar
2
@Iman Abidi: De acordo com o comentário de Oskar Berggren de 10 de setembro de 2014 às 15h51 no artigo "SQLHINTS- DateTime vs. DateTime2" que você referenciou: "datetime2 (3) NÃO é o mesmo que datetime. Eles terão o mesmo número de dígitos, mas a precisão do datetime é de 3,33 ms, enquanto a precisão do datetime2 (3) é de 1 ms. "
Tom
1
@PankajParkar: Uau, não tão rápido. Você pode consultar a seção Contras da minha resposta, datada de 10/7/17 abaixo.
Tom
Como datetime2usar menos espaço de armazenamento do que datetimeoferecer uma maior variedade e maior precisão?
Dai
107

Concordo com @marc_s e @Adam_Poward - DateTime2 é o método preferido para avançar. Possui um intervalo maior de datas, maior precisão e usa armazenamento igual ou menor (dependendo da precisão).

Uma coisa a discussão perdeu, no entanto ...
@Marc_s afirma: Both types map to System.DateTime in .NET - no difference there. Isso está correto, no entanto, o inverso não é verdadeiro ... e é importante ao fazer pesquisas no período (por exemplo, "encontre todos os registros modificados em 5/5/2010").

A versão do .NET Datetimetem alcance e precisão semelhantes aos DateTime2. Ao mapear um .net Datetimepara o SQL antigo, ocorreDateTime um arredondamento implícito . O SQL antigo DateTimetem precisão de 3 milissegundos. Isso significa que11:59:59.997 é o mais próximo possível do final do dia. Qualquer valor superior é arredondado para o dia seguinte.

Tente o seguinte:

declare @d1 datetime   = '5/5/2010 23:59:59.999'
declare @d2 datetime2  = '5/5/2010 23:59:59.999'
declare @d3 datetime   = '5/5/2010 23:59:59.997'
select @d1 as 'IAmMay6BecauseOfRounding', @d2 'May5', @d3 'StillMay5Because2msEarlier'

Evitar esse arredondamento implícito é um motivo significativo para mudar para DateTime2. O arredondamento implícito de datas causa claramente confusão:

EBarr
fonte
14
Você também pode evitar esse arredondamento ao não tentar encontrar o "fim" de um dia. > = 5 de maio E <6 de maio é muito mais seguro e funcionará em qualquer um dos tipos de data / hora (exceto TIME, é claro). Sugira também evitar formatos regionais ambíguos, como m / d / aaaa.
Aaron Bertrand
2
@AaronBertrand - concordo totalmente, mas analisando o número de perguntas que temos, parece que vale a pena descrever.
EBarr
1
Por que você mudou de 20100505para 5/5/2010? O formato anterior funcionará com qualquer região do SQL Server. O último irá quebrar: SET LANGUAGE French; SELECT Convert(datetime, '1/7/2015')oops:2015-07-01 00:00:00.000
ErikE 23/09
1
@EBarr: Re. "DateTime2 é o método preferido para avançar. Ele tem um intervalo mais amplo de datas, maior precisão e usa armazenamento igual ou menor (dependendo da precisão": discordo totalmente. Consulte a seção Contras da minha resposta de 7/10/17 abaixo Em resumo, esses benefícios provavelmente não são necessários (aplicativos de engenharia / científicos externos) e, portanto, não valem a perda de benefícios MUITO mais provavelmente necessários, a capacidade muito mais fácil (mesmo considerando soluções alternativas) de converter implicitamente / explicitamente em um numérico de ponto flutuante ( # de dias, incluindo se aplicável, frações desde a data e hora mín.) para +, - e avg.
Tom
20

Quase todas as respostas e comentários foram pesadas sobre os profissionais e leves sobre os contras. Aqui está uma recapitulação de todos os prós e contras até agora, além de alguns contras cruciais (no # 2 abaixo) que eu já vi mencionados uma vez ou não.

  1. PROS:

1.1 Mais compatível com ISO (ISO 8601) (embora eu não saiba como isso entra em jogo na prática).

1.2 Mais alcance (1/1/0001 a 31/12/9999 vs. 1/1 / 1753-12 / 31/9999) (embora o intervalo extra, todos anteriores ao ano 1753, provavelmente não seja usado, exceto por ex., em aplicativos históricos, astronômicos, geológicos etc.).

1.3 Corresponde exatamente ao intervalo do tipo do .NET DateTime(embora ambos convertam para frente e para trás sem codificação especial se os valores estiverem dentro do intervalo e da precisão do tipo de destino, exceto a Con # 2.1 abaixo, caso contrário, ocorrerá erro / arredondamento).

1.4 Mais precisão (100 nanossegundos, também conhecido como 0,000,000,1 seg. Vs. 3,33 milissegundos, também conhecido como 0,003,33 seg.) (Embora a precisão extra provavelmente não seja usada, exceto, por exemplo, em aplicativos de engenharia / científicos).

1.5 Quando configurado para precisão semelhante (como em 1 milissegundo, não "igual" (como em 3,33 milissegundos) como Iman Abidi afirmou) DateTime, usa menos espaço (7 x 8 bytes), mas é claro que você perderia o benefício de precisão que é provavelmente um dos dois (o outro sendo o intervalo) mais elogiado, embora prováveis ​​benefícios desnecessários).

  1. CONTRAS:

2.1 Ao passar um Parâmetro para um .NET SqlCommand, você deve especificar System.Data.SqlDbType.DateTime2se pode estar passando um valor fora do DateTimeintervalo e / ou precisão do SQL Server , porque o padrão é System.Data.SqlDbType.DateTime.

2.2 Não pode ser implicitamente / facilmente convertido em um valor numérico de ponto flutuante (número de dias desde a data e hora mín.) Para fazer o seguinte nas expressões do SQL Server usando valores e operadores numéricos:

2.2.1 adicione ou subtraia o número de dias ou dias parciais. Nota: Usar o DateAddFunction como solução alternativa não é trivial quando você precisa considerar várias, se não todas, partes da data e hora.

2.2.2 faça a diferença entre duas datas para fins de cálculo da "idade". Nota: Você não pode simplesmente usar a DateDiffFunção do SQL Server , porque ela não calcula agecomo a maioria das pessoas esperaria, pois se as duas datas atravessarem um limite de calendário / relógio das unidades especificadas, mesmo que por uma pequena fração dessa unidade, vai voltar a diferença de que, como uma unidade contra a 0. Por exemplo, o DateDiffna Day's de duas vezes data apenas 1 milissegundo além retornará 1 vs 0 (dias), se essas data vezes são em diferentes dias do calendário (por exemplo, “1999-12-31 23: 59: 59.9999999” e “2000-01-01 00: 00: 00.0000000”). A mesma diferença de 1 milissegundo de data e hora, se movida para que não cruzem um dia do calendário, retornará um "DateDiff" em Day0 (dias).

2.2.3 adote as Avgdatas e horários (em uma Consulta Agregada) simplesmente convertendo para "Flutuar" primeiro e depois novamente para DateTime.

NOTA: Para converter DateTime2para um numérico, é necessário fazer algo como a seguinte fórmula, que ainda assume que seus valores não são inferiores ao ano de 1970 (o que significa que você está perdendo todo o intervalo extra mais outros 217 anos. Nota: você pode não é possível simplesmente ajustar a fórmula para permitir um intervalo extra, pois você pode encontrar problemas de estouro numérico.

25567 + (DATEDIFF(SECOND, {d '1970-01-01'}, @Time) + DATEPART(nanosecond, @Time) / 1.0E + 9) / 86400.0- Fonte: " https://siderite.dev/blog/how-to-translate-t-sql-datetime2-to.html "

Obviamente, você também Castpode DateTimeprimeiro (e, se necessário, voltar a DateTime2), mas perderia os benefícios de precisão e alcance (todos anteriores ao ano de 1753) DateTime2vs., DateTimeque são os dois maiores e também ao mesmo tempo o 2 provavelmente menos necessário, o que sugere a questão de por que usá-lo quando você perde as conversões implícitas / fáceis para numérico de ponto flutuante (número de dias) para adição / subtração / benefício "idade" (vs. DateDiff) / Avgcálculos, que é grande em minha experiência.

Aliás, a Avgdata e hora é (ou pelo menos deveria ser) um caso de uso importante. a) Além de ser usado para obter duração média quando as datas e horários (desde uma data-base comum) são usadas para representar a duração (uma prática comum), b) também é útil obter uma estatística do tipo painel sobre qual a data média- time está na coluna de data e hora de um intervalo / grupo de linhas. c) Uma consulta ad-hoc padrão (ou pelo menos deve ser padrão) para monitorar / solucionar problemas de valores em uma coluna que pode não ser mais válida / mais e / ou talvez precise ser preterida é listar para cada valor a contagem de ocorrências e (se disponível) os Min, Avge Maxselos de data e hora associado a esse valor.

Tom
fonte
1
Como a visão contrária - ela indica o lado c # da equação. Combinado com todos os outros "profissionais", permitirá que as pessoas façam uma boa escolha com base no local em que desejam aliviar sua dor.
EBarr
1
@EBarr: Apenas a parte contras # 1 da minha "'visão contrária'" "aponta o lado c # da equação". O restante (Contras # 2.2.1 - 2.2.3), que, como eu disse, são os benefícios provavelmente mais necessários (de DateTime), todos relacionados aos efeitos nas consultas e declarações do SQL Server.
Tom
Re 2.2.1 - é considerado uma prática insegura fazer aritmética em datas, e a maneira preferida é sempre usar o DateAdd e funções relacionadas. Essa é a melhor prática. Há sérias responsabilidades em fazer aritmética de datas, e não menos importante é que não funciona para a maioria dos tipos de datas. Alguns artigos: sqlservercentral.com/blogs/… sqlblog.org/2011/09/20/…
RBerman
@RBerman: Re. "inseguro": só é inseguro com certos tipos de datas (como o que DateTime2eu já mencionei (devido à grande chance de estouro)). Ré. "não funciona para a maioria dos tipos de datas": você só precisa trabalhar com um, e a maioria das datas na maioria dos aplicativos provavelmente nunca precisará ser convertida em outro tipo de data durante toda a vida útil (exceto, talvez, como eu também mencionei , DateTime2para DateTime(por exemplo, fazer "aritmética em datas"; P). Dado isso, não vale a pena toda a codificação extra em consultas de pesquisa programadas, mas também ad-hoc, para usar um tipo de data não-aritmética.
Tom
15

DateTime2 causa estragos se você for um desenvolvedor do Access tentando escrever Now () no campo em questão. Acabei de fazer uma migração do Access -> SQL 2008 R2 e colocou todos os campos de data e hora como DateTime2. Anexando um registro com Now () como o valor bombardeado. Tudo bem em 1/1/2012 14:53:04, mas não em 10/01/2012 14:53:04.

Uma vez que o personagem fez a diferença. Espero que ajude alguém.

Rhett A Brown
fonte
15

Aqui está um exemplo que mostra as diferenças no tamanho do armazenamento (bytes) e na precisão entre smalldatetime, datetime, datetime2 (0) e datetime2 (7):

DECLARE @temp TABLE (
    sdt smalldatetime,
    dt datetime,
    dt20 datetime2(0),
    dt27 datetime2(7)
)

INSERT @temp
SELECT getdate(),getdate(),getdate(),getdate()

SELECT sdt,DATALENGTH(sdt) as sdt_bytes,
    dt,DATALENGTH(dt) as dt_bytes,
    dt20,DATALENGTH(dt20) as dt20_bytes,
    dt27, DATALENGTH(dt27) as dt27_bytes FROM @temp

que retorna

sdt                  sdt_bytes  dt                       dt_bytes  dt20                 dt20_bytes  dt27                         dt27_bytes
2015-09-11 11:26:00  4          2015-09-11 11:25:42.417  8         2015-09-11 11:25:42  6           2015-09-11 11:25:42.4170000  8

Portanto, se eu quiser armazenar informações no segundo - mas não no milissegundo - eu posso salvar 2 bytes cada se usar datetime2 (0) em vez de datetime ou datetime2 (7).

Baodad
fonte
10

embora exista maior precisão com datetime2 , alguns clientes não suportam data , hora ou datetime2 e forçam você a converter em um literal de cadeia de caracteres. Especificamente, a Microsoft menciona problemas de ODBC, OLE DB, JDBC e SqlClient de "nível inferior" com esses tipos de dados e possui um gráfico mostrando como cada um pode mapear o tipo.

Se valorizar compatibilidade acima da precisão, use data e hora

FistOfFury
fonte
10

Pergunta antiga ... Mas quero acrescentar algo que ainda não foi declarado por ninguém aqui ... (Nota: Esta é minha própria observação, portanto, não peça nenhuma referência)

Datetime2 é mais rápido quando usado nos critérios de filtro.

TLDR:

No SQL 2016, eu tinha uma tabela com cem mil linhas e uma coluna de data e hora ENTRY_TIME porque era necessário armazenar o tempo exato em segundos. Ao executar uma consulta complexa com muitas junções e uma subconsulta, quando usei a cláusula where como:

WHERE ENTRY_TIME >= '2017-01-01 00:00:00' AND ENTRY_TIME < '2018-01-01 00:00:00'

A consulta foi boa inicialmente quando havia centenas de linhas, mas quando o número de linhas aumentou, a consulta começou a fornecer este erro:

Execution Timeout Expired. The timeout period elapsed prior
to completion of the operation or the server is not responding.

Eu removi a cláusula where e, inesperadamente, a consulta foi executada em 1 segundo, embora agora todas as linhas de todas as datas tenham sido buscadas. Eu executo a consulta interna com a cláusula where, e demorou 85 segundos, e sem a cláusula where, levou 0,01 segundos.

Me deparei com muitos threads aqui para esse problema como desempenho de filtragem de data e hora

Otimizei a consulta um pouco. Mas a velocidade real que obtive foi alterando a coluna datetime para datetime2.

Agora, a mesma consulta que atingiu o tempo limite anterior leva menos de um segundo.

Felicidades

MJ Khan
fonte
9

A interpretação das sequências de datas datetimee também datetime2pode ser diferente ao usar DATEFORMATconfigurações que não são dos EUA . Por exemplo

set dateformat dmy
declare @d datetime, @d2 datetime2
select @d = '2013-06-05', @d2 = '2013-06-05'
select @d, @d2

Isso retorna 2013-05-06(ou seja, 6 de maio) para datetimee 2013-06-05(ou seja, 5 de junho) para datetime2. No entanto, com dateformatdefinido como mdy, ambos @de @d2retorno 2013-06-05.

O datetimecomportamento parece estar em desacordo com a documentação do MSDN dos SET DATEFORMATquais afirma: Alguns formatos de cadeias de caracteres, por exemplo ISO 8601, são interpretados independentemente da configuração DATEFORMAT . Obviamente não é verdade!

Até que fui mordido por isso, sempre pensei que as yyyy-mm-dddatas seriam tratadas corretamente, independentemente das configurações de idioma / localidade.

Richard Fawcett
fonte
2
Não. Para a ISO 8601, acho que você quis dizer AAAAMMDD (sem traços). SET LANGUAGE FRENCH; DECLARE @d DATETIME = '20130605'; SELECT @d;Tente novamente com os traços.
Aaron Bertrand
1
O padrão permite os formatos AAAA-MM-DD e AAAAMMDD para representações de data do calendário. Eu acho que o MSDN deve ser mais específico sobre qual subconjunto da especificação ISO 8601 é interpretado de forma independente!
Richard Fawcett
1
Eu sei disso, mas no SQL Server apenas a sintaxe sem traços é segura.
Aaron Bertrand
6

De acordo com este artigo , se você deseja ter a mesma precisão do DateTime usando o DateTime2, basta usar o DateTime2 (3). Isso deve fornecer a mesma precisão, ocupar um número a menos de bytes e fornecer um intervalo expandido.

jKlaus
fonte
Para ser claro, é a mesma precisão que o datetime do SQL, não um .NET DateTime.
Sam Rueby 13/11/2015
Isso está correto, presumi que todos entenderiam o contexto, mas vale a pena declarar especificamente.
Jklaus
4

Eu apenas tropecei em mais uma vantagem DATETIME2: evita um bug no adodbapimódulo Python , que explode se um datetimevalor de biblioteca padrão for passado, que possui microssegundos diferentes de zero para uma DATETIMEcoluna, mas funciona bem se a coluna estiver definida como DATETIME2.

Bob Kline
fonte
0
Select ValidUntil + 1
from Documents

O SQL acima não funcionará com um campo DateTime2. Ele retorna e erro "Tipo de operando em conflito: datetime2 é incompatível com int"

Adicionar 1 para o dia seguinte é algo que os desenvolvedores fazem com datas há anos. Agora, a Microsoft tem um super novo campo datetime2 que não pode lidar com essa funcionalidade simples.

"Vamos usar esse novo tipo que é pior que o antigo", acho que não!

Paul McCarthy
fonte
2
Apenas para esclarecer aqui, os tipos de dados datetimee datetime2foram introduzidos no SQL Server 2008. Você também obtém Operand type clash: date is incompatible with into datetipo que existe desde o dia. dateadd(dd, 1, ...)Porém, todos os três tipos de dados funcionam bem .
AlwaysLearning 21/08/19
2
Isto não está claro. Eu tenho um banco de dados SQLServer 2005 com um campo datetime.
Paul McCarthy
0

Eu acho que DATETIME2é a melhor maneira de armazenar o date, porque tem mais eficiência do que o DATETIME. Em SQL Server 2008você pode usar DATETIME2, ele armazena uma data e hora, leva de 6 bytesa 8 para armazenar e tem uma precisão de 100 nanoseconds. Portanto, qualquer pessoa que precise de maior precisão de tempo desejará DATETIME2.

James
fonte