Estou lendo a descrição do datetime
tipo de dados da documentação do Sybase ASE 15.7 :
As colunas datetime contêm datas entre 1º de janeiro de 1753 e 31 de dezembro de 9999. Os valores de datetime têm precisão de 1/300 segundo em plataformas que oferecem suporte a esse nível de granularidade.
Achei o acima muito confuso. Em primeiro lugar, este é o único tipo de dados para o qual é adicionada a advertência "em plataformas que suportam este nível de granularidade". O que exatamente isso significa e como descubro se minha plataforma é uma daquelas que "suporta esse nível de granularidade"?
Além disso, não está claro para mim o que significa efetivamente armazenar com precisão 1/300 de segundo. Estou acessando o banco de dados usando JDBC para o qual o único tipo plausível é java.sql.Timestamp . Esse tipo me permite recuperar com precisão de nanossegundos. Mas dado que a divisão por 300 requer dígitos infinitos no sistema decimal no caso geral, efetivamente um número fracionário de nanossegundos (com dígitos decimais infinitos) é necessário para manter um valor expresso como 1/300 de segundo. Portanto, isso significa que não consigo obter o valor armazenado no servidor sem perder alguma precisão, por mais insignificante que seja.
Por fim, quando executo a seguinte consulta:
SELECT convert(char(32), submission_date, 139) FROM some..table
Vejo valores como os seguintes:
Jan 6 2014 12:36:12.420000
Sep 12 2013 13:44:57.100000
Sep 10 2014 13:47:02.240000
Sep 10 2014 13:47:07.850000
Sep 10 2014 13:47:13.346000
Sep 10 2014 13:47:19.033000
Sep 10 2014 13:47:24.533000
Sep 10 2014 13:47:30.030000
Sep 10 2014 13:47:35.636000
Sep 10 2014 13:47:41.136000
Sep 10 2014 13:47:46.750000
Sep 10 2014 13:47:52.240000
Sep 25 2014 09:01:18.426000
Esses valores parecem indicar que apenas milésimos de segundo inteiros são mantidos (não 1/300 de segundo - o que exigiria um número fracionário de milésimos). Se fosse o caso de o servidor armazenar internamente os valores "com precisão de 1/300 segundo", eu esperaria que a conversão para uma notação decimal usasse todas as casas decimais disponíveis (exceto para os casos de borda de 3/300 segundos, 30/300 segundos, 150/300 segundos e alguns outros que não requerem uma quantidade infinita de dígitos no sistema decimal).
datetime
no Adaptive Server Enterprise (e no SQL Server, pois compartilham uma base de código comum que inclui o tipo datetime) é armazenado usando 8 bytes. 4 bytes para a data e 4 bytes para a hora. Você pode ver isso observando a versão binária de um datetime:Os quatro valores binários são:
Veja o seguinte, que mostra onde o 1/300 de segundo entra em jogo:
A diferença entre o 2º e o 3º valores, é uma:
Assim, as datas são armazenadas nos 4 bytes mais significativos; e os tempos são armazenados nos 4 bytes menos significativos. Embora mover para uma precisão maior que 3 milissegundos (1/300 de segundo) seja possível em 4 bytes de armazenamento; essa é toda a precisão que é realmente usada.
No SQL Server, você pode usar um
datetime2(7)
tipo de dados para obter precisão de até 7 dígitos, com precisão de 100ns:O armazenamento desses valores é um pouco diferente, mas você ainda pode ver o valor binário aumentando:
Estou usando o cliente Sybase ISQL; Utilitário Sybase CTISQL/15.7/P-EBF20996 SP100/DRV.15.7.0.10
datetime
Como um aparte, existe uma pequena diferença entre os valores de arredondamento do Adaptive Server Enterprise (ASE) e do SQL Server . No SQL Server, milissegundos são arredondados para0.000
,0.003
e0.007
, enquanto no ASE eles são arredondados para0.000
,0.003
e0.006
- por que há uma diferença não está documentada até onde eu sei. Você pode ver isso no ASE executando esta consulta:Que retorna:
Enquanto no SQL Server, o código equivalente,
SELECT CONVERT(varchar(50), CONVERT(datetime, N'2017-01-01T23:59:59.997'), 109);
, retorna:Com base nessa resposta , fica claro que a codificação exata é opaca e contra-intuitiva. Também está claro que a declaração na documentação do Sybase no sentido de que
datetime
pode conter valoresaccurate to 1/300 second
é enganosa e imprecisa.O que a documentação deveria ter dito é que
datetime
tem uma resolução/precisão/granularidade (escolha seu termo favorito) de aproximadamente 1/300 de segundo ou aproximadamente 3 ms .Fiz um teste para confirmar isso. Em uma mesa com aproximadamente 1000
datetime
valores (gerados aleatoriamente, pois dependem da entrada do usuário), eu queria ver quantos valores distintos de milissegundos eu encontraria:A instrução SQL mais interna na consulta acima também foi usada na minha pergunta e produz os valores que publiquei na minha pergunta.
O resultado da consulta composta foi 237, o que é consistente com o acima.
Além disso, a documentação do SQL Server (que é aplicável, pois a
datetime
implementação faz parte da base de código compartilhada entre o Sybase ASE e o SQL Server), diz o seguinte:Segue imagem:
Na mesma página é ainda assinalado que:
No geral, acho que usar um tipo tão peludo é um antipadrão. Não consigo entender por que alguém simplesmente não usaria um
INT
para armazenar segundos desde a Epoch (sim, eu sei sobre 2038), ou aBIGINT
para armazenar milissegundos ou mesmo microssegundos ou nanossegundos desde a Epoch.atualização após testes exaustivos
Consegui encontrar algum tempo para fazer testes exaustivos sobre esse assunto e consegui chegar ao fundo disso. Aqui estão minhas descobertas (testadas em relação ao Sybase ASE 15.7, acho que os mesmos resultados se aplicam ao SQL Server):
Provei experimentalmente que o servidor mantém
Datetime
valores fornecidos a ele pelo cliente com resolução de 1/300 de segundo. Criei uma tabela com o seguinte DDL:… e preenchê-lo (usando um programa de driver em Java) com 1000 valores com incrementos de milissegundos de 1/1000 de segundo. Executando a seguinte consulta:
… rendeu exatamente
300
.O acima estabelece que o servidor é capaz de armazenar apenas 300 valores distintos para a segunda parte fracionária. Isso não prova que os valores reais mantidos são múltiplos de 1/300 de segundo, mas é uma indicação razoável.
O que o acima implica é que qualquer armazenamento de segundos fracionários fornecido pelo cliente ao servidor é potencialmente com perdas. Portanto, fiz alguns testes adicionais para estabelecer exatamente o quão com perdas. O que encontrei é o seguinte:
O acima é consistente com a hipótese do servidor armazenar o componente de tempo como um número inteiro de 1/300s de segundo como:
n/300
n/300
n/300
.Por fim, fica claro que há algo errado com a
convert
função conforme a consulta:… relata zeros além do terceiro ponto decimal, o que claramente não é correto e confuso. Mas não acredito que refute o que foi dito acima.
atualização II
Estranhamente, a porcentagem relatada pelo meu código de teste para mapeamentos de servidor não exatos para valores de milissegundos é de 90% apenas quando uso em meus testes o driver Sybase jConnect JDBC . Para o driver JTDS JDBC de código aberto, aplicam -se 70% aprimorados e maravilhosamente redondos. Basicamente, a lógica de teste é que eu crio um valor com precisão de milissegundos no lado do cliente, armazeno-o no servidor e, em seguida, leio-o novamente do cliente para ver o que recebo. Esse arranjo de teste permite que o driver do lado do cliente corrija suavemente alguns valores do servidor no caminho de volta (quando o código do lado do cliente for lido no banco de dados).
Aparentemente, o driver JTDS pode explicar de maneira mais inteligente a maneira idiossincrática em que o servidor Sybase armazena o segundo componente fracionário e pode fazê-lo para todos os 300 valores distintos do servidor (compensando o erro de arredondamento na direção reversa - do servidor para o cliente - conforme necessário), não apenas para aqueles 100 que correspondem exatamente a algum valor de milissegundo na faixa de 0-1000.
De qualquer forma, isso não altera os fatos no lado do servidor e só é relevante se houver controle sobre o idioma e o driver do lado do cliente que serão usados para extrair
Datetime
valores do servidor.Pensei em pular essa parte para manter as coisas simples, mas então pensei em adicioná-la para completar. Também corrobora o modelo de armazenamento de
Datetime
valores do servidor, pois todos esses números (300, 90%, 70%) se encaixam na narrativa. Mas a vida é muito curta, hora de seguir em frente.