Melhor maneira de armazenar o tempo (hh: mm) em um banco de dados


100

Desejo armazenar os horários em uma tabela de banco de dados, mas só preciso armazenar as horas e os minutos. Sei que poderia usar DATETIME e ignorar os outros componentes da data, mas qual é a melhor maneira de fazer isso sem armazenar mais informações do que realmente preciso?


Qual é o equivalente a TIME para sql server 2005?
Erran Morad

@BoratSagdiyev não existe nenhum no SQL Server 2005, é por isso que fiz a pergunta.
Matthew Dresser

Respostas:


135

Você pode armazená-lo como um número inteiro do número de minutos após a meia-noite:

por exemplo.

0 = 00:00 
60 = 01:00
252 = 04:12

No entanto, você precisaria escrever algum código para reconstituir o tempo, mas isso não deve ser complicado.


8
Em seguida, use DATEADD () para obter os tempos reais de volta. Até mesmo o smallint seria o suficiente.
Joel Coehoorn

36
estive lá, fiz isso ... minutos = dd% 60 e horas = dd / 60 em ints resolve.
Osama Al-Maadeed em

2
Essa é uma ideia excelente, simples e direta. +1
whiskeysierra

7
uma pequena desvantagem é a falta de legibilidade no banco de dados
Jowen

precisamos armazenar algumas variantes de dias, horas e minutos; o tipo de dados Time no SQL Server vai até 23:59:59, portanto, não poderíamos usar isso. Inspirados por sua resposta, decidimos ter três colunas inteiras para dias: horas: minutos para flexibilidade máxima. obrigado!
matao de


15

DATETIME início DATETIME final

Eu imploro que você use dois valores DATETIME em vez disso, rotulados como algo como event_start e event_end .

O tempo é um negócio complexo

A maior parte do mundo já adotou o sistema métrico baseado em denery para a maioria das medições, correta ou incorretamente. Isso é bom no geral, porque pelo menos todos podemos concordar que ag, é um ml, é um cm cúbico. Pelo menos aproximadamente isso. O sistema métrico tem muitas falhas, mas pelo menos é internacionalmente falho e consistente.

Com o tempo, porém, temos; 1000 milissegundos em um segundo, 60 segundos a um minuto, 60 minutos a uma hora, 12 horas para cada meio dia, aproximadamente 30 dias por mês que variam de acordo com o mês e até mesmo o ano em questão, cada país tem seu tempo deslocado dos outros , a forma como a hora é formatada varia em cada país.

É muito para digerir, mas é impossível para um cenário tão complexo ter uma solução simples.

Alguns cantos podem ser cortados, mas há aqueles em que é mais sensato não

Embora a primeira resposta aqui sugira que armazenar um número inteiro de minutos após a meia-noite possa parecer perfeitamente razoável, aprendi a evitar fazer isso da maneira mais difícil.

As razões para implementar dois valores DATETIME são para um aumento na precisão, resolução e feedback.

Tudo isso é muito útil para quando o design produz resultados indesejáveis.

Estou armazenando mais dados do que o necessário?

Pode parecer que há mais informações sendo armazenadas do que eu preciso, mas há um bom motivo para aceitar isso.

Armazenar essas informações extras quase sempre acaba me economizando tempo e esforço a longo prazo, porque inevitavelmente descubro que quando alguém é informado de quanto tempo algo demorou, ele também vai querer saber quando e onde o evento aconteceu.

É um planeta enorme

No passado, fui culpado de ignorar que existem outros países neste planeta além do meu. Parecia uma boa ideia na época, mas SEMPRE resultou em problemas, dores de cabeça e perda de tempo posteriormente. SEMPRE considere todos os fusos horários.

C #

Um DateTime é renderizado perfeitamente para uma string em C #. O método ToString (string Format) é compacto e fácil de ler.

Por exemplo

new TimeSpan(EventStart.Ticks - EventEnd.Ticks).ToString("h'h 'm'm 's's'")

Servidor SQL

Além disso, se você estiver lendo seu banco de dados separado da interface do aplicativo, é agradável ler o dateTimes de relance e realizar cálculos neles é simples.

Por exemplo

SELECT DATEDIFF(MINUTE, event_start, event_end)

ISO8601 data padrão

Se estiver usando SQLite, você não tem isso, então use um campo Texto e armazene-o no formato ISO8601, por exemplo.

"2013-01-27T12: 30: 00 + 0000"

Notas:

  • Este usa relógio de 24 horas *

  • A parte do deslocamento de tempo (ou +0000) do ISO8601 mapeia diretamente para o valor da longitude de uma coordenada GPS (não levando em consideração o horário de verão ou o país).

Por exemplo

TimeOffset=(±Longitude.24)/360 

... onde ± se refere à direção leste ou oeste.

Portanto, vale a pena considerar se valeria a pena armazenar longitude, latitude e altitude junto com os dados. Isso vai variar na aplicação.

  • ISO8601 é um formato internacional.

  • O wiki é muito bom para mais detalhes em http://en.wikipedia.org/wiki/ISO_8601 .

  • A data e a hora são armazenadas na hora internacional e o deslocamento é registrado dependendo de onde no mundo a hora foi armazenada.

Na minha experiência, sempre há a necessidade de armazenar a data e a hora completas, independentemente de eu achar que há quando eu começo o projeto. ISO8601 é uma maneira muito boa e à prova de futuro de fazer isso.

Conselhos adicionais grátis

Também vale a pena agrupar eventos como uma cadeia. Por exemplo, se registrar uma corrida, todo o evento pode ser agrupado por piloto, corrida_circuito, pontos de verificação de circuito e voltas_circuito.

Na minha experiência, também é aconselhável identificar quem armazenou o registro. Como uma tabela separada preenchida por meio do gatilho ou como uma coluna adicional na tabela original.

Quanto mais você investe, mais você ganha

Eu entendo perfeitamente o desejo de ser o mais econômico possível com espaço, mas raramente o faria à custa de perder informações.

Uma regra prática com bancos de dados é, como o título diz, um banco de dados só pode dizer o quanto tem dados para, e pode ser muito caro voltar aos dados históricos, preenchendo lacunas.

A solução é acertar na primeira vez. Certamente é mais fácil falar do que fazer, mas agora você deve ter uma visão mais profunda do design de banco de dados eficaz e, subsequentemente, ter uma chance muito maior de acertar da primeira vez.

Quanto melhor for o seu projeto inicial, menos custosos serão os reparos mais tarde.

Só digo isso porque, se pudesse voltar no tempo, é o que diria a mim mesmo quando chegasse lá.


2
Seu comentário "A parte +0000 do ISO8601 mapeia diretamente para a latitude em uma coordenada GPS" está errado. Representa o deslocamento do UTC
Gary Walker

10

Basta armazenar uma data e hora regular e ignorar todo o resto. Por que gastar mais tempo escrevendo código que carregue um int, o manipule e o converta em um datetime, quando você poderia simplesmente carregar um datetime?


3
Uma possível razão pode ser para economizar espaço no disco rígido - um DATETIMEtipo de dados leva 4 bytes para armazenar, enquanto um, SMALLINTpor exemplo, leva apenas um quarto disso. Não há grande diferença se você tiver apenas alguns milhares de linhas, mas se tiver muitos milhões de linhas como muitas empresas, sua economia de espaço será substancial.
Sheridan

32
Possivelmente, mas considere que 12 pessoas responderam a essa pergunta, cada uma levando, digamos, 15 minutos para pensar a respeito. Suponha que todos sejam programadores e ganhem $ 50 por hora. Com o custo do tempo gasto apenas para pensar sobre esse problema, você poderia ter comprado um disco rígido novinho em folha de 2 TB para armazenar os bytes extras.
Seth

@Seth você está certo se falarmos apenas sobre bytes extras. Mas sua sugestão pode ser apenas se for um projeto pequeno com 1-2 desenvolvedores. Mas será um grande projeto com muitos desenvolvedores (mais QA), seria um grande problema quando um novo programador tentar descobrir isso. Infelizmente, todos nós dependemos da lógica de negócios.
Mediador

Com os serviços em nuvem, economizar espaço em disco pode ser importante nos casos em que você paga por byte lido / processado.
RedShift de

2
Outro problema divertido é se você está contando com o componente de tempo, ele não levará em consideração os ajustes de horário de verão quando usado em diferentes épocas do ano.
Chris Seufert

4

já que você não mencionou nada se estiver no SQL Server 2008, você pode usar o tipo de dados de hora, caso contrário use minutos desde a meia-noite


3

Na verdade, o SQL Server armazena o tempo em frações de um dia. Por exemplo, 1 dia inteiro = valor de 1. 12 horas é um valor de 0,5.

Se você deseja armazenar o valor da hora sem utilizar um tipo DATETIME, armazenar a hora na forma decimal seria adequado para essa necessidade, ao mesmo tempo que torna a conversão para DATETIME simples.

Por exemplo:

SELECT CAST(0.5 AS DATETIME)
--1900-01-01 12:00:00.000

Armazenar o valor como DECIMAL (9,9) consumiria 5 bytes. No entanto, se a precisão não for de extrema importância, um REAL consumirá apenas 4 bytes. Em ambos os casos, o cálculo agregado (isto é, tempo médio) pode ser facilmente calculado em valores numéricos, mas não em tipos de dados / tempo.


"Na verdade, o SQL Server armazena o tempo em frações de um dia." Acho que armazena dias desde (ou antes) 01-jan-1900 nos primeiros 4 bytes e o tempo, em milissegundos, nos segundos 4 bytes. (SmallDateTime usa 2 bytes para cada um com intervalo de datas e minutos mais estreitos, em vez de milissegundos para o tempo)
Kristen

Eu sei (99,99% de certeza) que para a hora do dia são frações.
graham.reeds

2

Gostaria de convertê-los em um número inteiro (HH * 3600 + MM * 60) e armazená-lo dessa forma. Tamanho de armazenamento pequeno e ainda fácil de trabalhar.


2

Se você estiver usando o MySQL, use um tipo de campo TIME e a funcionalidade associada que vem com TIME.

00:00:00 é o formato de hora unix padrão.

Se você tiver que olhar para trás e revisar as tabelas manualmente, os números inteiros podem ser mais confusos do que um registro de data e hora real.


1

Experimente smalldatetime. Pode não dar o que você deseja, mas irá ajudá-lo em suas necessidades futuras de manipulação de data / hora.


se @mdresser estivesse usando <MSSQL 2005, o servidor passaria por "valor smalldatetime fora do intervalo"
Fergus

1

Tem certeza de que precisará apenas das horas e minutos? Se você quiser fazer algo significativo com ele (como, por exemplo, computar intervalos de tempo entre dois desses pontos de dados), não ter informações sobre fusos horários e DST pode dar resultados incorretos. Os fusos horários talvez não se apliquem ao seu caso, mas o DST certamente se aplicará.


1

Em vez de minutos após a meia-noite, nós o armazenamos como relógio de 24 horas, como um SMALLINT.

09:12 = 912 14:15 = 1415

ao converter de volta para a "forma legível por humanos", apenas inserimos dois pontos ":" dois caracteres da direita. Preencher com zeros à esquerda, se necessário. Salva a matemática de cada maneira e usa alguns bytes a menos (em comparação com varchar), além de garantir que o valor seja numérico (em vez de alfanumérico)

Muito idiota ... deveria ter havido um tipo de dados TIME no MS SQL por muitos anos já IMHO ...


Kristen está absolutamente correta, mas apenas se sua suposição sobre as horas e minutos que representam apenas um único período de 24 horas estiver correta. 10 bits são necessários para armazenar 0..1439 minutos. Isso significa que há 6 bits adicionais que podem ser usados ​​como quiser, então há espaço para ser criativo. Sugiro usar 5 bits para armazenar o deslocamento de hora para o fuso horário em que você está, mas apenas se o autor da pergunta quiser armazenar no máximo 23:59 (um minuto para a meia-noite)
WonderWorker

1

O que acho que você está pedindo é uma variável que armazenará os minutos como um número. Isso pode ser feito com os vários tipos de variável inteira:

SELECT 9823754987598 AS MinutesInput

Em seguida, em seu programa, você pode simplesmente ver isso na forma que desejar, calculando:

long MinutesInAnHour = 60;

long MinutesInADay = MinutesInAnHour * 24;

long MinutesInAWeek = MinutesInADay * 7;


long MinutesCalc = long.Parse(rdr["MinutesInput"].toString()); //BigInt converts to long. rdr is an SqlDataReader.   


long Weeks = MinutesCalc / MinutesInAWeek;

MinutesCalc -= Weeks * MinutesInAWeek;


long Days = MinutesCalc / MinutesInADay;

MinutesCalc -= Days * MinutesInADay;


long Hours = MinutesCalc / MinutesInAnHour;

MinutesCalc -= Hours * MinutesInAnHour;


long Minutes = MinutesCalc;

Surge um problema quando você solicita o uso de eficiência. Mas, se você tiver pouco tempo, então use um BigInt anulável para armazenar o valor dos minutos.

Um valor nulo significa que a hora ainda não foi registrada.

Agora, vou explicar na forma de uma viagem de ida e volta ao espaço sideral.

Infelizmente, uma coluna da tabela armazenará apenas um único tipo. Portanto, você precisará criar uma nova tabela para cada tipo, conforme necessário.

Por exemplo:

  • Se MinutesInput = 0 .. 255 , use TinyInt (converter conforme descrito acima).

  • Se MinutesInput = 256 .. 131071 então use SmallInt (Observação: o valor mínimo de SmallInt é -32.768. Portanto, negue e adicione 32768 ao armazenar e recuperar o valor para utilizar a faixa completa antes de converter como acima).

  • Se MinutesInput = 131072 .. 8589934591 , use Int (Observação: negue e adicione 2147483648 conforme necessário).

  • Se MinutesInput = 8589934592 .. 36893488147419103231 então use BigInt (Observação: adicione e negue 9223372036854775808 conforme necessário).

  • Se MinutesInput> 36893488147419103231 então eu pessoalmente usaria VARCHAR (X) aumentando X conforme necessário, pois um caractere é um byte. Terei que revisitar essa resposta posteriormente para descrever isso por completo (ou talvez um colega stackoverflowee possa terminar esta resposta).

Como cada valor sem dúvida exigirá uma chave exclusiva, a eficiência do banco de dados só será aparente se a faixa dos valores armazenados for uma boa combinação entre muito pequeno (perto de 0 minutos) e muito alto (maior que 8589934591).

Até que os valores sendo armazenados realmente alcancem um número maior que 36893488147419103231, você também pode ter uma única coluna BigInt para representar seus minutos, já que não será necessário desperdiçar um Int em um identificador exclusivo e outro int para armazenar o valor dos minutos.


0

A economia de tempo no formato UTC pode ajudar melhor, como sugeriu Kristen.

Certifique-se de que está usando o relógio de 24 horas porque não há nenhum meridiano AM ou PM para ser usado no UTC.

Exemplo:

  • 04:12 - 0412
  • 10:12 - 1012
  • 14h28 - 1428
  • 23h56 - 2356

Ainda é preferível usar o formato padrão de quatro dígitos.


0

Armazene tickscomo long/ bigint, que atualmente são medidos em milissegundos. O valor atualizado pode ser encontrado examinando o TimeSpan.TicksPerSecondvalor.

A maioria dos bancos de dados tem um tipo DateTime que armazena automaticamente a hora como ticks nos bastidores, mas no caso de alguns bancos de dados, por exemplo, SqlLite, armazenar ticks pode ser uma forma de armazenar a data.

A maioria dos idiomas permite a conversão fácil de TicksTimeSpanTicks.

Exemplo

Em C #, o código seria:

long TimeAsTicks = TimeAsTimeSpan.Ticks;

TimeAsTimeSpan = TimeSpan.FromTicks(TimeAsTicks);

Esteja ciente, porém, porque no caso do SqlLite, que oferece apenas um pequeno número de tipos diferentes, que são; INT, REALe VARCHARSerá necessário armazenar o número de carrapatos como uma string ou duas INTcélulas combinadas. Isso ocorre porque um INTé um número assinado de 32 bits, enquanto BIGINTum número assinado de 64 bits.

Nota

Minha preferência pessoal, entretanto, seria armazenar a data e a hora como uma ISO8601string.


-1

IMHO, qual é a melhor solução depende, até certo ponto, de como você armazena o tempo no resto do banco de dados (e no resto do seu aplicativo)

Pessoalmente, tenho trabalhado com SQLite e tento sempre usar carimbos de data / hora unix de / para armazenar o tempo absoluto, então, ao lidar com a hora do dia (como você pediu), faço o que Glen Solsberry escreve em sua resposta e armazeno o número de segundos desde a meia-noite

Ao adotar essa abordagem geral, as pessoas (inclusive eu!) Lendo o código ficam menos confusas se eu usar o mesmo padrão em todos os lugares

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.