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 SELECT
devuelve ambas filas, sin embargo, el segundo SELECT
solo devuelve una sola fila, que puede no ser el resultado deseado:
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;
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.