Forma preferida de almacenar DateTime


18

Podemos almacenar información de fecha y hora de dos maneras. ¿Cuál es el mejor enfoque para almacenar información de fecha y hora?

¿Almacenar fecha y hora en 2 columnas separadas o una columna usando DateTime ?

¿Puedes explicar por qué ese enfoque es mejor?

(Enlace a documentos de MySQL para referencia, la pregunta es general, no específica de MySQL)
Tipos de fecha y hora : fecha y hora


3
Eso depende en gran medida del sistema de base de datos que esté utilizando. Para lo que vale: Oracle eligió hacer esto como una columna (como un tipo de datos DATETIME), en cuyo caso, usar su soporte incorporado ciertamente será superior a almacenar esa información en 2 columnas como NUMBER tipos de datos (incluso si solo necesita 1 parte para una consulta dada ... la fecha o la hora).
Kris Johnston

55
Para SQL Server, un caso en el que se puede preferir la división es para agrupar por fecha. Un agregado corriente será capaz de ser usado sin una especie para el índice compuesto en date,time con group by date, pero no para un índice en datetime con group by cast(datetime as date)pesar de que suministraría el orden deseado.
Martin Smith

1
Tenga en cuenta que cualquier cálculo matemático de los valores de Tiempo requiere conocer la fecha y la zona horaria; por ejemplo, la distancia entre dos veces depende de si ese día contiene un evento de horario de verano, algunos días tienen 23 o 25 horas, y también existen segundos intermedios.
Peteris

Respuestas:


23

Almacenar los datos en una sola columna es la forma preferida, ya que están inextricablemente vinculados. Un punto en el tiempo es una sola pieza de información, no dos.

Una forma común de almacenar datos de fecha / hora, empleados "detrás de escena" por muchos productos, es convirtiéndolos en un valor decimal donde la "fecha" es la parte entera del valor decimal, y el "tiempo" es la fracción valor. Entonces, 1900-01-01 00:00:00 se almacena como 0.0 y 20 de septiembre de 2016 9:34:00 se almacena como 42631.39861. 42631 es el número de días desde 1900-01-01. .39861 es la porción de tiempo transcurrido desde la medianoche. No use un tipo decimal directamente para hacer esto, use un tipo explícito de fecha / hora; mi punto aquí es solo una ilustración.

Almacenar los datos en dos columnas separadas significa que deberá combinar los dos valores de las columnas cada vez que desee ver si un punto dado en el tiempo es anterior o posterior al valor almacenado.

Si almacena los valores por separado, invariablemente se encontrará con "errores" que son difíciles de detectar. Tome por ejemplo lo siguiente:

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');

En el código anterior, estamos creando una tabla de prueba, rellenándola con dos valores, luego realizando una consulta simple contra esos datos. El primero SELECTdevuelve ambas filas, sin embargo, el segundo SELECTsolo devuelve una sola fila, que puede no ser el resultado deseado:

ingrese la descripción de la imagen aquí

La forma correcta de filtrar un rango de fecha / hora donde los valores están en columnas discretas, como lo señala @ypercube en los comentarios, es:

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')
    )

Si necesita separar el componente de tiempo para fines de análisis , podría considerar agregar una columna calculada y persistente para la parte de tiempo del valor:

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

SELECT *
FROM #dt;

ingrese la descripción de la imagen aquí

La columna persistente se podría indexar permitiendo clasificaciones rápidas, etc., por hora del día.

Si está considerando dividir la fecha y la hora en dos campos para fines de visualización, debe tener en cuenta que el formateo debe realizarse en el cliente, no en el servidor.


11

Voy a proporcionar una opinión disidente a las otras respuestas.

Si los componentes de fecha y hora se requieren juntos, es decir, una entrada no es válida si contiene uno pero no el otro (o es NULL en uno pero no en el otro), entonces almacenarlo en una sola columna tiene sentido por las razones dadas en otro respuestas

Sin embargo, puede darse el caso de que uno o ambos componentes sean individualmente opcionales. En ese caso, sería incorrecto almacenarlo en una sola columna. Hacerlo te obligaría a representar valores NULL de manera arbitraria, por ejemplo, almacenar la hora como 00:00:00.

Aquí hay un par de ejemplos:

  • Está registrando viajes en vehículos para deducciones de impuestos por milla. Sería útil conocer la hora exacta del viaje, pero si un empleado no lo anota y lo ha olvidado, la fecha aún debe registrarse por sí misma (fecha requerida, hora opcional).

  • Está realizando una encuesta para averiguar a qué hora las personas comen su almuerzo, y solicita a los participantes que completen un formulario con una muestra de sus horas de almuerzo, incluidas las fechas. Algunos no se molestan en completar la fecha, y no desea descartar los datos, ya que son las horas que realmente le importan (fecha opcional, hora requerida).

Vea esta pregunta relacionada para enfoques alternativos.


En RFC 3339 hay una convención para grabar "desplazamiento local desconocido". No creo que cubra el caso de uso de "tiempo desconocido", pero está cerca. La siguiente sección "hora local no calificada" está aún más cerca, pero nuevamente no es suficiente.
Geneorama

Sí, estoy mirando el barril de refactorizar mi esquema debido a esto en este momento. Tome una situación de alquiler de automóviles. Para recoger un automóvil de una empresa de alquiler, la empresa debe estar abierta; así que especifica una fecha y hora para la recolección. Sin embargo, muchos tienen cajas de keydrop; así que te vas después de las horas. Entonces, si la ubicación está cerrada los domingos; hay una fecha de entrega; pero no un tiempo Almacenar un valor 0 (por ejemplo, 12 a.m.) no funcionará porque algunas ubicaciones están abiertas hasta la medianoche, que es un valor válido en otras situaciones.
Reece

5

Siempre preferiré almacenar eso como una sola columna a menos que haya una demanda específica de negocios / aplicaciones. A continuación están mis puntos:

  • Extraer el tiempo de la marca de tiempo no es un problema
  • ¿Por qué agregar una columna adicional solo por tiempo si podemos almacenar ambos juntos?
  • Para evitar agregar Fecha y Hora cada vez que realice una consulta.

1
@a_horse_with_no_name tiene un punto aquí. Creo que "Extraer marca de tiempo de fecha y hora no es un problema" debería reformularse como "Extraer tiempo de marca de tiempo no es un problema" . "Marca de tiempo" generalmente significa fecha y hora (y generalmente zona horaria).
ypercubeᵀᴹ

Sí, de acuerdo @ ypercubeᵀᴹ. La marca de tiempo generalmente significa fecha y hora. Mencioné explícitamente la palabra DateTimeStamp, para que cualquiera pueda entender que estamos hablando tanto de fecha como de hora. Pero también tienes razón. Modificado la respuesta.
Ashwini Mohan

3

En SQL Server, es mejor almacenar DataTime como un campo. Si crea un índice en la columna DataTime, puede usarse como búsqueda de fecha y como búsqueda de fecha y hora. Por lo tanto, si necesita limitar todos los registros que existen para la fecha específica, aún puede usar el índice sin tener que hacer nada especial. Si necesita consultar la porción de tiempo, no podrá usar el mismo índice y, por lo tanto, si tiene un caso de negocios en el que le importa más la hora del día que DateTime, debe almacenarlo por separado, ya que deberá crear un índice sobre él y mejorar el rendimiento.


1

De hecho, es una pena que no haya un tipo de DBMS cruzado estándar para esto (como INT y VARCHAR son para enteros y valores de cadena). Los 2 enfoques de bases de datos cruzadas que he conocido hasta ahora usan columnas VARCHAR / CHAR para almacenar valores de DataTime como cadenas formateadas de acuerdo con el estándar ISO 8601 (más conveniente, legible para humanos) y BIGINT para almacenarlas como marcas de tiempo POSIX (almacenadas más eficiente, más rápido, más fácil de manipular matemáticamente).


2
Sí lo hay: timestampeso es lo que define el estándar SQL. Almacenar marcas de tiempo como cadenas es un muy mal consejo
a_horse_with_no_name

0

Después de leer un montón de cosas, el tiempo UTC Unix en BIGINT parece ser la solución óptima. ID de TZDB timesone en VARCHAR para almacenamiento de zona horaria si es necesario. Algunos argumentos:

  1. TIMESTAMP y DATETIME realizan un montón de conversiones efectivas en segundo plano que parecen ser complejas y no claras. El servidor cambia de la hora local a UTC o a la hora del servidor y viceversa, a veces o no. Un montón de gastos generales ocultos para cada función.

  2. BIGINT (8kb) es al menos tan ligero o más ligero que DECIMAL requerido para el almacenamiento en formato xxxxxx.xxxxxx, que MySQL prácticamente almacena como dos INTs + algo . Y es suficiente para almacenar siglos por delante.

  3. Casi todos los principales lenguajes de programación tienen bibliotecas de funciones estándar para trabajar con el tiempo Unix.

  4. Las operaciones matemáticas con BIGINT deberían ser tan rápidas o más rápidas que cualquier otra cosa en cualquier hardware.

Por supuesto, todo lo anterior es relevante para grandes proyectos internacionales. Para algo pequeño, ir con el formato predeterminado del marco elegido parece ser lo suficientemente bueno.


2
" hacer un montón de conversiones efectivas en el fondo que parecen estar ... no claras " - ¿de qué DBMS estás hablando? Para una timestampcolumna no se producen "conversiones efectivas" (en la capa de la base de datos) y paratimestamp with time zone esto está bien documentado y explicado en los manuales (al menos para Oracle y Postgres)
a_horse_with_no_name

1
"Casi todos los principales lenguajes de programación tienen bibliotecas de funciones estándar para trabajar con el tiempo Unix". Y, sin embargo, descarta todas las bibliotecas y funciones sobre fechas, fecha y hora que tiene SQL / DBMS, con la opción de utilizar bigint ...
ypercubeᵀᴹ
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.