Maneira preferida de armazenar DateTime

18

Podemos armazenar informações de data e hora de duas maneiras. Qual é a melhor abordagem para armazenar informações DateTime?

Armazenando Data e Hora em 2 colunas separadas ou uma coluna usando DateTime ?

Você pode explicar por que essa abordagem é melhor?

(Link para os documentos do MySQL para referência, a pergunta é geral, não específica ao MySQL)
Tipos de data e hora : Data e hora

Julian
fonte
3
Isso depende muito do sistema de banco de dados que você está usando. Por que vale a pena: a Oracle escolheu fazer isso como uma coluna (como um tipo de dados DATETIME); nesse caso, o uso do suporte interno certamente será superior a armazenar essas informações em 2 colunas como NUMBER tipos de dados (mesmo se você apenas precisa de 1 parte para uma determinada consulta ... a data ou a hora).
Kris Johnston
5
Para o SQL Server, um caso em que a divisão pode ser preferida é o agrupamento por data. Um agregado corrente irá ser capaz de ser usado sem uma espécie para o índice composto em date,time com group by date, mas não por um índice em datetime com group by cast(datetime as date)ainda que forneceria o fim desejado.
Martin Smith
11
Observe que qualquer cálculo matemático dos valores de hora exige o conhecimento da data e do fuso horário - por exemplo, a distância entre duas vezes depende do momento em que o dia contém um evento de horário de verão, alguns dias têm 23 ou 25 horas e também existem segundos bissextos.
Peteris

Respostas:

23

Armazenar os dados em uma única coluna é a maneira preferida, pois eles estão inextricavelmente vinculados. Um ponto no tempo é uma única informação, não duas.

Uma maneira comum de armazenar dados de data / hora, empregados "nos bastidores" por muitos produtos, é convertendo-os em um valor decimal em que a "data" é a parte inteira do valor decimal e a "hora" é o fracionário valor. Portanto, 1900-01-01 00:00:00 é armazenado como 0.0 e 20 de setembro de 2016 9:34:00 é armazenado como 42631.39861. 42631 é o número de dias desde 1900-01-01. .39861 é a parte do tempo decorrido desde a meia-noite. Não use um tipo decimal diretamente para fazer isso, use um tipo explícito de data / hora; meu ponto aqui é apenas uma ilustração.

Armazenar os dados em duas colunas separadas significa que você precisará combinar os dois valores da coluna sempre que quiser ver se um determinado momento é anterior ou posterior ao valor armazenado.

Se você armazenar os valores separadamente, invariavelmente encontrará "bugs" difíceis de detectar. Tome, por exemplo, o seguinte:

IF OBJECT_ID('tempdb..#DT') IS NOT NULL
DROP TABLE #DT;
CREATE TABLE #DT
(
    dt_value DATETIME NOT NULL
    , d_value DATE NOT NULL
    , t_value TIME(0) NOT NULL
);


DECLARE @d DATETIME = '2016-09-20 09:34:00';

INSERT INTO #DT (dt_value, d_value, t_value)
SELECT @d, CONVERT(DATE, @d), CONVERT(TIME(0), @d);

SET @d = '2016-09-20 11:34:00';

INSERT INTO #DT (dt_value, d_value, t_value)
SELECT @d, CONVERT(DATE, @d), CONVERT(TIME(0), @d);

/* show all rows with a date after 2016-07-01 11:00 am */
SELECT *
FROM #DT dt
WHERE dt.dt_value >= '2016-07-01 11:00:00';

/* show all rows with a date after 2016-07-01 11:00 am */
SELECT *
FROM #DT dt
WHERE dt.d_value >= CONVERT(DATE, '2016-07-01')
    AND dt.t_value >= CONVERT(TIME(0), '11:00:00');

No código acima, estamos criando uma tabela de teste, preenchendo-a com dois valores e, em seguida, realizando uma consulta simples nesses dados. O primeiro SELECTretorna as duas linhas; no entanto, o segundo SELECTretorna apenas uma única linha, que pode não ser o resultado desejado:

insira a descrição da imagem aqui

A maneira correta de filtrar um intervalo de data / hora em que os valores estão em colunas discretas, conforme apontado por @ypercube nos comentários, é:

WHERE dt.d_value > CONVERT(DATE, '2016-07-01') /* note there is no time component here */
    OR (
        dt.d_value = CONVERT(DATE, '2016-07-01') 
        AND dt.t_value >= CONVERT(TIME(0), '11:00:00')
    )

Se você precisar separar o componente de tempo para fins de análise , considere adicionar uma coluna calculada e persistente para a parte de tempo do valor:

ALTER TABLE #DT
ADD dt_value_time AS CONVERT(TIME(0), dt_value) PERSISTED;

SELECT *
FROM #dt;

insira a descrição da imagem aqui

A coluna persistida pode então ser indexada, permitindo classificações rápidas, etc., por hora do dia.

Se você estiver pensando em dividir a data e a hora em dois campos para fins de exibição, deve perceber que a formatação deve ser feita no cliente, não no servidor.

Max Vernon
fonte
11

Vou fornecer uma opinião divergente para as outras respostas.

Se os componentes de data e hora forem necessários juntos, ou seja, uma entrada é inválida se contiver um, mas não o outro (ou for NULL em um, mas não no outro), o armazenamento em uma única coluna fará sentido pelas razões indicadas em outros respostas.

No entanto, pode ser que um ou ambos os componentes sejam individualmente opcionais. Nesse caso, seria incorreto armazená-lo em uma única coluna. Fazer isso forçaria você a representar valores NULL de maneira arbitrária, por exemplo, armazenando o horário como 00:00:00.

Aqui estão alguns exemplos:

  • Você está gravando viagens de veículo para deduções de imposto de quilometragem. Saber a hora exata da jornada seria útil, mas se um funcionário não a anotasse e a esquecesse, a data ainda deve ser registrada por si mesma (data obrigatória, hora opcional).

  • Você está conduzindo uma pesquisa para descobrir a que horas as pessoas almoçam e solicita aos participantes que preencham um formulário com uma amostra de seus horários de almoço, incluindo datas. Alguns não se incomodam em preencher a data e você não deseja descartar os dados, pois são os horários que realmente interessam (data opcional, tempo necessário).

Veja esta pergunta relacionada para abordagens alternativas.

JBentley
fonte
Na RFC 3339, existe uma convenção para gravar "deslocamento local desconhecido". Não acho que abranja completamente o caso de uso de "tempo desconhecido", mas está próximo. A próxima seção "hora local não qualificada" é ainda mais próxima, mas novamente não é suficiente.
geneorama 04/01
Sim, estou olhando para o barril de refatoração do meu esquema por causa disso agora. Tome uma situação de aluguel de carro. Para pegar um carro em uma locadora - a empresa precisa estar aberta; para que você especifique uma data e hora para a coleta. No entanto, muitos têm caixas de teclas; então você sai depois de horas. Portanto, se o local estiver fechado aos domingos; há uma data de entrega; mas não um tempo. Armazenar um valor 0 (por exemplo, 12h) não funcionará porque algum local fica aberto até meia-noite, o que é um valor válido em outras situações.
Reece
5

Eu sempre prefiro armazenar isso como uma única coluna, a menos que haja alguma demanda específica de negócios / aplicativos. Abaixo estão meus pontos -

  • Extrair o tempo do registro de data e hora não é um problema
  • Por que adicionar uma coluna extra apenas por um tempo, se podemos armazenar os dois juntos
  • Para evitar adicionar Data e Hora sempre que você estiver consultando.
Ashwini Mohan
fonte
11
@a_horse_with_no_name tem um ponto aqui. Eu acho que "Extrair o carimbo de data e hora do datetimestamp não é um problema" deve ser reformulado como "Extrair o carimbo de data e hora do timestamp não é um problema" . "Carimbo de data e hora" geralmente significa data e hora (e geralmente fuso horário).
ypercubeᵀᴹ
Sim, concorde @ ypercubeᵀᴹ. O carimbo de data / hora geralmente significa data e hora. Mencionei explicitamente a palavra DateTimeStamp, para que todos possam entender que estamos falando de data e hora. Mas você também está correto. Modificado a resposta.
Ashwini Mohan
3

No SQL Server, é melhor armazenar o DataTime como um campo. Se você criar um índice na coluna DataTime, ele poderá ser usado como pesquisa por Data e pesquisa por Data e Hora. Portanto, se você precisar limitar todos os registros existentes para a data específica, ainda poderá usar o índice sem precisar fazer nada de especial. Se você precisar consultar a parte do tempo, não poderá usar o mesmo índice e, portanto, se tiver um caso de negócios em que se preocupe mais com a hora do dia do que com o DateTime, armazene-o separadamente, pois precisará criar um índice e melhorar o desempenho.

Vladimir Oselsky
fonte
1

De fato, é uma pena que não haja um tipo de DBMS cruzado padrão para isso (como INT e VARCHAR são para números inteiros e valores de sequência). As duas abordagens entre bancos de dados que conheci até agora estão usando as colunas VARCHAR / CHAR para armazenar valores DataTime como seqüências de caracteres formatadas de acordo com o padrão ISO 8601 (mais conveniente, legível por humanos) e usando o BIGINT para armazená-los como registros de data e hora POSIX (armazenados mais eficiente, mais rápido, mais fácil de manipular matematicamente).

Ivan
fonte
2
Sim, existe: é timestampisso que o padrão SQL define. Armazenar timestamps como strings é um péssimo conselho
a_horse_with_no_name 25/02
0

Depois de ler várias coisas, o horário UTC do Unix no BIGINT parece ser a solução ideal. ID de horário TZDB em VARCHAR para armazenamento de fuso horário, se necessário. Alguns argumentos:

  1. TIMESTAMP e DATETIME realizam várias conversões enganosas em segundo plano que parecem complexas e não claras. O servidor alterna do horário local para o UTC ou para o horário do servidor e vice-versa, às vezes ou não. Um monte de sobrecarga oculta para todas as funções.

  2. O BIGINT (8kb) é pelo menos tão leve ou mais leve que o DECIMAL necessário para o armazenamento no formato xxxxxx.xxxxxx, que é praticamente armazenado como dois INTs + algo pelo MySQL . E basta armazenar séculos à frente.

  3. Praticamente todas as principais linguagens de programação possuem bibliotecas de funções padrão para trabalhar com o tempo Unix.

  4. As operações matemáticas com o BIGINT devem ser mais rápidas ou rápidas do que qualquer outra coisa em qualquer hardware.

É claro que tudo isso é relevante para grandes projetos internacionais. Para algo pequeno, seguir o formato padrão da estrutura escolhida parece ser bom o suficiente.

Arthur Tarasov
fonte
2
" faz um monte de conversões enigmáticas em segundo plano que parecem ... não claras " - de que DBMS você está falando? Para uma timestampcoluna, nenhuma "conversão enganosa" acontece (na camada do banco de dados) e timestamp with time zoneisso é bem documentado e explicado nos manuais (pelo menos para Oracle e Postgres)
a_horse_with_no_name
11
"Praticamente todas as principais linguagens de programação têm bibliotecas de funções padrão para trabalhar com o tempo Unix." E ainda assim você jogar fora todas as bibliotecas e funções sobre datas, datetimes e marcas de tempo que SQL / DBMS tem, com sua escolha de usar bigint ...
ypercubeᵀᴹ