La mejor forma de almacenar el tiempo (hh: mm) en una base de datos

100

Quiero almacenar horas en una tabla de base de datos, pero solo necesito almacenar horas y minutos. Sé que podría usar DATETIME e ignorar los otros componentes de la fecha, pero ¿cuál es la mejor manera de hacer esto sin almacenar más información de la que realmente necesito?

Matthew Dresser
fuente
¿Cuál es el equivalente de TIME para SQL Server 2005?
Erran Morad
@BoratSagdiyev no hay uno en SQL Server 2005, por eso hice la pregunta.
Matthew Dresser

Respuestas:

135

Puede almacenarlo como un número entero del número de minutos después de la medianoche:

p.ej.

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

Sin embargo, necesitaría escribir algún código para reconstituir el tiempo, pero eso no debería ser complicado.

Salto Grande
fuente
8
Luego use DATEADD () para recuperar los tiempos reales. Incluso una pequeña cantidad sería suficiente.
Joel Coehoorn
36
estado allí, hecho eso ... mins = dd% 60 y hours = dd / 60 en ints hace el truco.
Osama Al-Maadeed
2
Esa es una idea genial, simple y directa. +1
whiskeysierra
7
una pequeña desventaja es la falta de legibilidad en la base de datos
Jowen
necesitamos almacenar alguna variante de días, horas y minutos; el tipo de datos Time en SQL Server solo sube a 23:59:59, por lo que no pudimos usar eso. Inspirados por su respuesta, decidimos tener tres columnas int para días: horas: minutos para una máxima flexibilidad. ¡Gracias!
matao
15

DATETIME inicio DATETIME fin

Le imploro que use dos valores DATETIME en su lugar, etiquetados como event_start y event_end .

El tiempo es un asunto complejo

La mayor parte del mundo ha adoptado ahora el sistema métrico basado en denery para la mayoría de las mediciones, con razón o sin ella. Esto es bueno en general, porque al menos todos podemos estar de acuerdo en que ag, es un ml, es un cm cúbico. Al menos aproximadamente así. El sistema métrico tiene muchas fallas, pero al menos tiene fallas consistentes a nivel internacional.

Sin embargo, con el tiempo tenemos; 1000 milisegundos en un segundo, 60 segundos a un minuto, 60 minutos a una hora, 12 horas por cada medio día, aproximadamente 30 días por mes que varían según el mes e incluso el año en cuestión, cada país tiene su diferencia horaria de los demás , la forma en que se formatea la hora en cada país varía.

Es mucho para digerir, pero en resumen, es imposible que un escenario tan complejo tenga una solución simple.

Se pueden cortar algunas esquinas, pero hay aquellas en las que es más prudente no

Aunque la respuesta principal aquí sugiere que almacenar un número entero de minutos después de la medianoche puede parecer perfectamente razonable, he aprendido a evitar hacerlo de la manera difícil.

Las razones para implementar dos valores DATETIME son un aumento en la precisión, resolución y retroalimentación.

Todos estos son muy útiles cuando el diseño produce resultados no deseados.

¿Estoy almacenando más datos de los necesarios?

Al principio, puede parecer que se está almacenando más información de la que necesito, pero hay una buena razón para recibir este golpe.

Almacenar esta información adicional casi siempre termina ahorrándome tiempo y esfuerzo a largo plazo, porque inevitablemente encuentro que cuando a alguien se le dice cuánto tiempo tomó algo, también querrá saber cuándo y dónde tuvo lugar el evento.

Es un planeta enorme

En el pasado, he sido culpable de ignorar que hay otros países en este planeta además del mío. Parecía una buena idea en ese momento, pero esto SIEMPRE ha provocado problemas, dolores de cabeza y una pérdida de tiempo más adelante. SIEMPRE considere todas las zonas horarias.

C#

Un DateTime se representa muy bien en una cadena en C #. El método ToString (formato de cadena) es compacto y fácil de leer.

P.ej

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

Servidor SQL

Además, si está leyendo su base de datos por separado de la interfaz de su aplicación, entonces dateTimes es agradable de leer de un vistazo y realizar cálculos en ellos es sencillo.

P.ej

SELECT DATEDIFF(MINUTE, event_start, event_end)

Estándar de fecha ISO8601

Si usa SQLite, entonces no tiene esto, así que use un campo de texto y guárdelo en formato ISO8601, por ejemplo.

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

Notas:

  • Esto usa un reloj de 24 horas *

  • La parte de la compensación de tiempo (o +0000) de ISO8601 se asigna directamente al valor de longitud de una coordenada de GPS (sin tener en cuenta el horario de verano ni en todo el país).

P.ej

TimeOffset=(±Longitude.24)/360 

... donde ± se refiere a la dirección este u oeste.

Por lo tanto, vale la pena considerar si valdría la pena almacenar la longitud, latitud y altitud junto con los datos. Esto variará según la aplicación.

  • ISO8601 es un formato internacional.

  • La wiki es muy buena para obtener más detalles en http://en.wikipedia.org/wiki/ISO_8601 .

  • La fecha y la hora se almacenan en hora internacional y la compensación se registra según el lugar del mundo en que se almacenó la hora.

En mi experiencia, siempre existe la necesidad de almacenar la fecha y la hora completas, independientemente de si creo que existe cuando comienzo el proyecto. ISO8601 es una forma muy buena y preparada para el futuro de hacerlo.

Asesoramiento adicional gratis

También vale la pena agrupar los eventos como una cadena. Por ejemplo, si se graba una carrera, todo el evento podría agruparse por corredor, circuito de carrera, puntos de control de circuito y vueltas de circuito.

En mi experiencia, también es aconsejable identificar quién almacenó el registro. Ya sea como una tabla separada que se completa mediante un activador o como una columna adicional dentro de la tabla original.

Cuanto más pones, más sales

Entiendo completamente el deseo de ser lo más económico posible con el espacio, pero rara vez lo haría a costa de perder información.

Una regla empírica con las bases de datos es, como dice el título, una base de datos solo puede decirle lo que tiene de datos, y puede ser muy costoso revisar los datos históricos, llenando los vacíos.

La solución es hacerlo bien a la primera. Ciertamente, es más fácil decirlo que hacerlo, pero ahora debería tener una visión más profunda del diseño eficaz de la base de datos y, posteriormente, tener una oportunidad mucho mayor de hacerlo bien la primera vez.

Cuanto mejor sea su diseño inicial, menos costosas serán las reparaciones más adelante.

Solo digo todo esto, porque si pudiera retroceder en el tiempo, es lo que me diría cuando llegué allí.

WonderWorker
fuente
2
Su comentario "La parte +0000 de la ISO8601 se asigna directamente a la latitud en una coordenada GPS" es incorrecta. Representa el desplazamiento de UTC
Gary Walker
10

Simplemente almacene una fecha y hora regular e ignore todo lo demás. ¿Por qué dedicar más tiempo a escribir código que carga un int, lo manipula y lo convierte en una fecha y hora, cuando podría simplemente cargar una fecha y hora?

Seth
fuente
3
Una posible razón podría ser para ahorrar espacio en el disco duro: un DATETIMEtipo de datos tarda 4 bytes en almacenarse, mientras que, SMALLINTpor ejemplo, solo ocupa una cuarta parte. No hay gran diferencia si solo tiene unos pocos miles de filas, pero si tiene muchos millones de filas como lo hacen muchas empresas, el ahorro de espacio será sustancial.
Sheridan
32
Posiblemente, pero considere que 12 personas respondieron esta pregunta, cada una de las cuales se tomó, digamos, 15 minutos para pensar en ello. Suponga que todos son programadores y ganan $ 50 por hora. Con el costo del tiempo dedicado a pensar en este problema, podría haber comprado un nuevo y brillante disco duro de 2TB para almacenar los bytes adicionales.
Seth
@ Tiene razón si hablamos solo de bytes extra. Pero su sugerencia puede ser solo si se trata de un proyecto pequeño con 1-2 desarrolladores. Pero será un gran proyecto con muchos desarrolladores (además de control de calidad), sería un gran problema cuando un nuevo programador intenta resolver esto. Por desgracia, todos dependemos de la lógica empresarial.
Mediador
Con los servicios en la nube, ahorrar espacio en disco puede ser importante en los casos en los que paga por byte leído / procesado.
RedShift
2
Otro problema divertido es que si confía en el componente de tiempo, no tendrá en cuenta los ajustes de horario de verano cuando se use en diferentes épocas del año.
Chris Seufert
4

como no lo mencionó, si está en SQL Server 2008, puede usar el tipo de datos de tiempo; de lo contrario, use minutos desde la medianoche

SQLMenace
fuente
3

SQL Server en realidad almacena el tiempo como fracciones de un día. Por ejemplo, 1 día completo = valor de 1. 12 horas es un valor de 0.5.

Si desea almacenar el valor de la hora sin utilizar un tipo DATETIME, almacenar la hora en forma decimal se adaptaría a esa necesidad, al mismo tiempo que simplifica la conversión a DATETIME.

Por ejemplo:

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

Almacenar el valor como DECIMAL (9,9) consumiría 5 bytes. Sin embargo, si la precisión no es de suma importancia, un REAL consumiría solo 4 bytes. En cualquier caso, el cálculo agregado (es decir, el tiempo medio) se puede calcular fácilmente en valores numéricos, pero no en tipos de datos / tiempo.

Taylor Gerring
fuente
"SQL Server en realidad almacena el tiempo como fracciones de día". Creo que almacena días desde (o antes) 01-Jan-1900 en los primeros 4 bytes y tiempo, en milisegundos, en los segundos 4 bytes. (SmallDateTime usa 2 bytes para cada uno con un rango de fechas más estrecho y minutos, en lugar de milisegundos para el tiempo)
Kristen
Sé (99,99% seguro) que para la hora del día son fracciones.
graham.reeds
2

Los convertiría en un número entero (HH * 3600 + MM * 60) y los almacenaría de esa manera. Tamaño de almacenamiento pequeño y todavía bastante fácil de trabajar.

Glen Solsberry
fuente
2

Si está utilizando MySQL, use un tipo de campo de TIME y la funcionalidad asociada que viene con TIME.

00:00:00 es el formato de hora estándar de Unix.

Si alguna vez tiene que mirar hacia atrás y revisar las tablas a mano, los números enteros pueden ser más confusos que una marca de tiempo real.

Sintaxis
fuente
1

Prueba smalldatetime. Puede que no le brinde lo que desea, pero lo ayudará en sus necesidades futuras en manipulaciones de fecha / hora.

Marlon Tribunal
fuente
si @mdresser estaba usando <MSSQL 2005, el servidor pasaría por un "valor de fecha y hora fuera de rango"
Fergus
1

¿Estás seguro de que solo necesitarás las horas y los minutos? Si desea hacer algo significativo con él (como, por ejemplo, calcular lapsos de tiempo entre dos de esos puntos de datos), no tener información sobre las zonas horarias y el horario de verano puede dar resultados incorrectos. Es posible que las zonas horarias no se apliquen en su caso, pero el horario de verano sin duda lo hará.

mghie
fuente
1

En lugar de minutos después de la medianoche, lo almacenamos como reloj de 24 horas, como SMALLINT.

09:12 = 912 14:15 = 1415

al convertir de nuevo a "formato legible por humanos", simplemente insertamos dos puntos ":" dos caracteres de la derecha. Botón izquierdo con ceros si es necesario. Guarda las matemáticas en todos los sentidos y utiliza unos pocos bytes menos (en comparación con varchar), además de que obliga a que el valor sea numérico (en lugar de alfanumérico)

Aunque bastante tonto ... debería haber habido un tipo de datos TIME en MS SQL durante muchos años ya en mi humilde opinión ...

Kristen
fuente
Kristen tiene toda la razón, pero solo si su suposición sobre las horas y los minutos que solo representan un período de 24 horas es correcta. Se requieren 10 bits para almacenar 0..1439 minutos. Esto significa que hay 6 bits adicionales que se pueden usar a su gusto, por lo que hay espacio para ser creativo. Sugeriría usar 5 bits para almacenar el desplazamiento de hora para la zona horaria en la que se encuentra, pero solo si el autor de la pregunta solo quisiera almacenar un tiempo máximo de 23:59 (un minuto para la medianoche)
WonderWorker
1

Lo que creo que está pidiendo es una variable que almacenará los minutos como un número. Esto se puede hacer con los diferentes tipos de variables enteras:

SELECT 9823754987598 AS MinutesInput

Luego, en su programa, simplemente puede ver esto en la forma que desee 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 un problema cuando solicita que se utilice la eficiencia. Pero, si tienes poco tiempo, simplemente use un BigInt anulable para almacenar su valor de minutos.

Un valor de nulo significa que la hora aún no se ha registrado.

Ahora, lo explicaré en forma de un viaje de ida y vuelta al espacio exterior.

Desafortunadamente, una columna de tabla solo almacenará un tipo. Por lo tanto, deberá crear una nueva tabla para cada tipo según sea necesario.

Por ejemplo:

  • Si MinutesInput = 0 .. 255 entonces use TinyInt (Convertir como se describe arriba).

  • Si MinutesInput = 256 .. 131071 , utilice SmallInt (Nota: el valor mínimo de SmallInt es -32,768. Por lo tanto, niegue y agregue 32768 cuando almacene y recupere el valor para utilizar el rango completo antes de convertir como se indicó anteriormente).

  • Si MinutesInput = 131072 .. 8589934591 , utilice Int (Nota: niegue y agregue 2147483648 según sea necesario).

  • Si MinutesInput = 8589934592 .. 36893488147419103231 , utilice BigInt (Nota: agregue y niegue 9223372036854775808 según sea necesario).

  • Si MinutesInput > 36893488147419103231 , personalmente usaría VARCHAR (X) aumentando X según sea necesario, ya que un carácter es un byte. Tendré que volver a visitar esta respuesta en una fecha posterior para describir esto en su totalidad (o tal vez un compañero stackoverflowee pueda terminar esta respuesta).

Dado que cada valor sin duda requerirá una clave única, la eficiencia de la base de datos solo será aparente si el rango de los valores almacenados es una buena combinación entre muy pequeño (cerca de 0 minutos) y muy alto (mayor que 8589934591).

Hasta que los valores que se almacenan realmente alcancen un número mayor que 36893488147419103231, entonces también podría tener una sola columna BigInt para representar sus minutos, ya que no necesitará desperdiciar un Int en un identificador único y otro int para almacenar el valor de los minutos.

WonderWorker
fuente
0

El ahorro de tiempo en formato UTC puede ayudar mejor, como sugirió Kristen.

Asegúrese de que está utilizando un reloj de 24 horas porque no hay meridianos AM o PM en UTC.

Ejemplo:

  • 4:12 a.m. - 0412
  • 10:12 a.m. - 1012
  • 2:28 p. M. - 1428
  • 11:56 p. M. - 2356

Sigue siendo preferible utilizar el formato estándar de cuatro dígitos.

balamurugavel
fuente
0

Almacene tickscomo un long/ bigint, que actualmente se mide en milisegundos. El valor actualizado se puede encontrar mirando elTimeSpan.TicksPerSecond valor.

La mayoría de las bases de datos tienen un tipo DateTime que almacena automáticamente la hora como tics entre bastidores, pero en el caso de algunas bases de datos, como SqlLite, almacenar ticks puede ser una forma de almacenar la fecha.

La mayoría de los idiomas permiten la conversión fácil de TicksTimeSpanTicks.

Ejemplo

En C # el código sería:

long TimeAsTicks = TimeAsTimeSpan.Ticks;

TimeAsTimeSpan = TimeSpan.FromTicks(TimeAsTicks);

Sin embargo, tenga en cuenta, porque en el caso de SqlLite, que solo ofrece una pequeña cantidad de tipos diferentes, que son; INT, REALy VARCHARserá necesario almacenar el número de ticks como una cadena o dos INTceldas combinadas. Esto es porque un INTes un número con signo de 32 bits, mientras que BIGINTes un número con signo de 64 bits.

Nota

Sin embargo, mi preferencia personal sería almacenar la fecha y la hora como una ISO8601cadena.

WonderWorker
fuente
-1

En mi humilde opinión, cuál es la mejor solución depende en cierta medida de cómo almacena el tiempo en el resto de la base de datos (y el resto de su aplicación)

Personalmente, he trabajado con SQLite y trato de usar siempre marcas de tiempo Unix para almacenar el tiempo absoluto, por lo que cuando trato con la hora del día (como usted pregunta) hago lo que Glen Solsberry escribe en su respuesta y almaceno la cantidad de segundos desde la medianoche.

Al adoptar este enfoque general, las personas (¡incluyéndome a mí!) Que leen el código se confunden menos si uso el mismo estándar en todas partes.

sunyata
fuente