Estoy construyendo una base de datos con Postgres donde habrá muchas agrupaciones de cosas por month
y year
, pero nunca por date
.
- Podría crear enteros
month
yyear
columnas y usarlos. - O podría tener una
month_year
columna y siempre establecer elday
1.
El primero parece un poco más simple y claro si alguien está mirando los datos, pero el segundo es bueno porque usa un tipo apropiado.
postgresql
database-design
datetime
David N. Welton
fuente
fuente
month
que contenga dos enteros. Pero creo que si nunca necesitas el día del mes, usar dos enteros probablemente sea más fácilRespuestas:
Personalmente, si es una fecha, o puede ser una fecha, sugiero que siempre la almacene como una. Es más fácil trabajar con él como regla general.
Puede tener una fecha que admitirá el día si alguna vez la necesita, o una
smallint
para año y mes que nunca admitirá la precisión adicional.Data de muestra
Veamos un ejemplo ahora ... Creemos 1 millón de fechas para nuestra muestra. Esto es aproximadamente 5,000 filas por 200 años entre 1901 y 2100. Cada año debería tener algo para cada mes.
Pruebas
Sencillo
WHERE
Ahora podemos probar estas teorías de no usar la fecha. Ejecuté cada una de estas veces para calentar las cosas.
Ahora, intentemos el otro método con ellos separados
Para ser justos, no todos son 0.749 ... algunos son un poco más o menos, pero no importa. Todos son relativamente iguales. Simplemente no es necesario.
En un mes
Ahora, divirtámonos ... Digamos que desea encontrar todos los intervalos dentro de 1 mes de enero de 2014 (el mismo mes que utilizamos anteriormente).
Compare eso con el método combinado
Es a la vez más lento y más feo.
GROUP BY
/ /ORDER BY
Método combinado,
Y nuevamente con el método compuesto
Conclusión
En general, deje que las personas inteligentes hagan el trabajo duro. Datemath es difícil, mis clientes no me pagan lo suficiente. Solía hacer estas pruebas. Me costaba mucho concluir que podría obtener mejores resultados que
date
. Dejé de intentarlo.ACTUALIZACIONES
@a_horse_with_no_name sugerido para mi prueba dentro de un mes
WHERE (year, month) between (2013, 12) and (2014,2)
. En mi opinión, si bien es una consulta más compleja, prefiero evitarla a menos que haya una ganancia. Por desgracia, aún fue más lento, aunque está cerca, lo que es más fácil de quitar de esta prueba. Simplemente no importa mucho.fuente
date
es el camino a seguir en la mayoría de los casos.Como alternativa al método propuesto por Evan Carroll, que considero probablemente la mejor opción, he usado en algunas ocasiones (y no especialmente cuando uso PostgreSQL) solo una
year_month
columna, de tipoINTEGER
(4 bytes), calculada comoEs decir, codifica el mes en los dos dígitos decimales más a la derecha (dígito 0 y dígito 1) del número entero, y el año en los dígitos 2 a 5 (o más, si es necesario).
Esta es, hasta cierto punto, la alternativa de un hombre pobre para construir su propio
year_month
tipo y operadores. Tiene algunas ventajas, principalmente "claridad de intención", y algunos ahorros de espacio (no en PostgreSQL, creo), y también algunos inconvenientes, al tener dos columnas separadas.Puede garantizar que los valores son válidos simplemente agregando un
Puede tener una
WHERE
cláusula que se vea así:y funciona de manera eficiente (si la
year_month
columna está indexada correctamente, por supuesto).Puede agrupar de
year_month
la misma manera que podría hacerlo con una fecha y con la misma eficiencia (al menos).Si necesita separarse
year
ymonth
, el cálculo es sencillo:Lo que es inconveniente : si desea agregar 15 meses a uno
year_month
, debe calcular (si no he cometido un error o supervisión):Si no tiene cuidado, esto puede ser propenso a errores.
Si desea obtener la cantidad de meses entre dos años-meses, debe hacer algunos cálculos similares. Eso es (con muchas simplificaciones) lo que realmente sucede debajo del capó con la aritmética de fechas, que afortunadamente se nos oculta a través de funciones y operadores ya definidos.
Si necesita muchas de estas operaciones, el uso
year_month
no es demasiado práctico. Si no lo hace, es una forma muy clara de aclarar su intención.Como alternativa, podría definir un
year_month
tipo, y definir un operadoryear_month
+interval
, y también otroyear_month
-year_month
... y ocultar los cálculos. En realidad nunca he hecho un uso tan intenso como para sentir la necesidad en la práctica. Adate
- endate
realidad te está ocultando algo similar.fuente
Como alternativa al método de joanolo =) (lo siento, estaba ocupado pero quería escribir esto)
ALEGRÍA
Vamos a hacer lo mismo, pero con bits. Uno
int4
en PostgreSQL es un entero con signo, que va desde -2147483648 hasta +2147483647Aquí hay una descripción general de nuestra estructura.
Almacenamiento mes.
pow(2,4)
es de 4 bits .Aquí está nuestro mapa de bits de dónde se almacenan los meses.
Meses, 1 de enero - 12 de diciembre
Años. Los 28 bits restantes nos permiten almacenar nuestra información anual
En este punto, necesitamos decidir cómo queremos hacer esto. Para nuestros propósitos, podríamos usar un desplazamiento estático, si solo necesitamos cubrir 5,000 AD, podríamos volver a lo
268,430,455 BC
que cubre casi todo Mesozoic y todo lo útil en el futuro.Y, ahora tenemos los rudimentos de nuestro tipo, que expiran en 2.700 años.
Así que manos a la obra para hacer algunas funciones.
Una prueba rápida muestra que esto funciona ...
Ahora tenemos funciones que podemos usar en nuestros tipos binarios.
Podríamos haber cortado un poco más de la parte firmada, almacenado el año como positivo, y luego haberlo ordenado naturalmente como un int firmado. Si la velocidad fuera una prioridad más alta que el espacio de almacenamiento, esa habría sido la ruta que seguimos. Pero por ahora, tenemos una fecha que funciona con el Mesozoico.
Puedo actualizar más tarde con eso, solo por diversión.
fuente