11

Estoy creando una base de datos de análisis (tengo una comprensión firme de los datos y los objetivos de negocio y solo de las habilidades de base de datos básicas a moderadas).Mesas de calendario en PostgreSQL 9

He encontrado algunas referencias a la construcción de almacenes similares que implementan el concepto de 'tablas de calendario'. Esto tiene sentido y es bastante fácil de hacer. La mayoría de los ejemplos que veo, sin embargo, son tablas de calendario que limitan el alcance a 'día'. Mis datos deberán analizarse a nivel de hora. Posiblemente minutos.

Mi pregunta: ¿Sería útil una implementación de tablas de calendario para la granularidad a nivel de hora/minuto en términos de eficiencia de espacio y velocidad de consulta/ordenamiento? Si es así, ¿puedes recomendar una estructura de tabla y un método/ejemplo de población?

Mi tabla de datos primaria contendrá más de 20 millones de filas de datos en un momento determinado y los subconjuntos típicos para el análisis se encuentran en el rango de 1 a 5 millones. Entonces, como pueden ver, son muchos campos de marca de tiempo.

Respuesta

8

Las tablas de calendario implementan una compensación de espacio/tiempo. Al usar más espacio, algunos tipos de consultas se ejecutan en menos tiempo, ya que pueden aprovechar los índices. Son seguros siempre que tenga cuidado con las restricciones de CHECK() y siempre que tenga procesos administrativos para encargarse de cualquier restricción que su dbms no admita.

Si su granularidad es de un minuto, deberá generar aproximadamente medio millón de filas por año. Una tabla de calendario mínima se vería así.

2011-01-01 00:00:00 
2011-01-01 00:01:00 
2011-01-01 00:02:00 
2011-01-01 00:03:00 
2011-01-01 00:04:00 

Si está haciendo un análisis de "cubo", es posible que esté mejor con algo como esto.

bucket_start   bucket_end 
-- 
2011-01-01 00:00:00 2011-01-01 00:01:00 
2011-01-01 00:01:00 2011-01-01 00:02:00 
2011-01-01 00:02:00 2011-01-01 00:03:00 
2011-01-01 00:03:00 2011-01-01 00:04:00 
2011-01-01 00:04:00 2011-01-01 00:05:00 

Dado que el operador BETWEEN de SQL incluye los extremos, por lo general debe evitar su uso. Esto se debe a que incluye los puntos finales, y es difícil expresar bucket_end como "bucket_start más un minuto, menos el menor tiempo que este servidor pueda reconocer". (El peligro es un valor que es un microsegundo mayor que bucket_end, pero aún menor que el siguiente valor para bucket_start.)

Si fuera a construir esa tabla, probablemente lo haría así. (Aunque yo creo más difícil acerca de si debería llamarlo "calendario".)

create table calendar (
    bucket_start timestamp primary key, 
    bucket_end timestamp unique, 
    CHECK (bucket_end = bucket_start + interval '1' minute) 
    -- You also want a "no gaps" constraint, but I don't think you 
    -- can do that in a CHECK constraint in PostgreSQL. You might 
    -- be able to use a trigger that counts the rows, and compares 
    -- that count to the number of minutes between min(bucket_start) 
    -- and max(bucket_start). Worst case, you can always run a report 
    -- that counts the rows and sends you an email. 
); 

La restricción UNIQUE crea un índice implícito en PostgreSQL.

Esta consulta insertará el valor de un día de filas (24 horas * 60 minutos) a la vez.

insert into calendar 
select coalesce(
       (select max(bucket_start) from calendar), 
       cast('2011-01-01 00:00:00' as timestamp) 
       ) 
      + cast((n || 'minute') as interval) as bucket_start, 
     coalesce(
       (select max(bucket_start) from calendar), 
       cast('2011-01-01 00:00:00' as timestamp) 
       ) 
      + cast((n + 1 || ' minute') as interval) as bucket_end 
from generate_series(1, (24*60)) n; 

Puede envolver eso en una función para generar un año a la vez. Probablemente intentaré comprometer menos de medio millón de filas a la vez.

No debería tomar demasiado tiempo para generar 20 millones de filas para probar, y otras 20 millones de filas de minutos "calendario". Almuerzo largo. Tal vez una tarde en el sol.

+0

What exactamente "ventaja de los índices" estás hablando? – Quassnoi

12

En PostgreSQL, puede generar tablas de calendario de longitud arbitraria y granularidad sobre la marcha:

SELECT CAST('2011-01-01' AS DATE) + (n || ' hour')::INTERVAL 
FROM generate_series(0, 23) n 

Esto no requiere recursión (como con los otros sistemas) y es un método preferido para generar los conjuntos de resultados volátiles .

+0

Sí, pero unir el resultado de generate_series() en quizás 20 millones de filas probablemente va a arrastrar el rendimiento por el inodoro. Las consultas en una tabla de calendario pueden aprovechar los índices. –

+1

@Catcall: las consultas en una tabla de calendario suelen suponer una combinación a la izquierda entre todos los valores en la tabla de calendario y algunos de los valores en la tabla de hechos, de modo que hay registros 'NULL' devueltos para los períodos sin registros de hechos. ¿Podría proporcionarnos una consulta de muestra que se beneficiaría al reemplazar 'generate_series' con una tabla real con datos? – Quassnoi

+0

Interesante. Gracias. Así que la idea es que podría crear tres de esas tablas: días, horas, minutos y mi tabla de conjunto de datos tendría claves como 'day_id', 'hour_id', 'minute_id' que podrían usarse juntas o de forma independiente dependiendo de la resolución de mi análisis? Si es así, eso es asombroso. Si no, me falta algo. –

1

En los almacenes de datos que construí, estaba usando las dimensiones CALENDARIO y HORA_OF_DAY por separado. La primera dimensión tiene una granularidad de día de 1 día y la segunda granularidad de 1 minuto.

En otros dos casos, sabía de antemano que no se requerirían informes en la granularidad inferior a 15 minutos. En ese caso, para simplificar, estaba usando una única dimensión CALENDARIO con 96 registros por día.

Estaba usando este enfoque en los almacenes de Oracle hasta ahora, pero podría estar involucrado en un proyecto de almacén PostgreSQL este verano.