Календарные таблицы в PostgreSQL 9 - PullRequest
13 голосов
/ 28 апреля 2011

Я создаю аналитическую базу данных (у меня есть четкое понимание данных и бизнес-целей и только базовые и средние навыки работы с базами данных).

Я сталкивался с некоторыми ссылками на создание аналогичных складов, которые реализуют концепцию «календарных таблиц». Это имеет смысл и достаточно легко сделать. Однако большинство примеров, которые я вижу, - это календарные таблицы, которые ограничивают область действия до «дня». Мои данные нужно будет проанализировать до часового уровня. Возможно минут.

Мой вопрос: будет ли полезна реализация календарных таблиц для детализации на уровне часов / минут с точки зрения эффективности использования пространства и скорости запросов / сортировки? Если да, можете ли вы порекомендовать структуру таблицы и метод / пример заполнения?

Моя первичная таблица данных будет содержать более 20 миллионов строк данных в любой момент времени, а типичные поднаборы для анализа находятся в диапазоне от 1 до 5 миллионов. Итак, как вы можете видеть, это много полей с метками времени.

Ответы [ 3 ]

14 голосов
/ 28 апреля 2011

В PostgreSQL вы можете на лету генерировать календарные таблицы произвольной длины и степени детализации:

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

Это не требует рекурсии (как в других системах) и является предпочтительным методом для генерацииизменчивые наборы результатов.

10 голосов
/ 28 апреля 2011

Таблицы календаря реализуют компромисс между пространством и временем.Используя больше места, некоторые виды запросов выполняются за меньшее время, потому что они могут использовать преимущества индексов.Они безопасны, если вы осторожны с ограничениями CHECK () и если у вас есть административные процессы, чтобы позаботиться о любых ограничениях, которые не поддерживает ваша dbms.

Если ваша гранулярность равнаодну минуту, вам нужно будет генерировать около полумиллиона строк для каждого года.Минимальная таблица календаря будет выглядеть следующим образом.

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

Если вы выполняете анализ "корзины", вам может быть лучше что-то вроде этого.

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

Так как SQL BETWEENОператор включает в себя конечные точки, вам обычно нужно избегать его использования.Это потому, что он включает в себя конечные точки, и трудно выразить bucket_end как «bucket_start плюс одну минуту, минус наименьшее количество времени, которое этот сервер может распознать».(Опасность - это значение, которое на микросекунду больше, чем bucket_end, но все же меньше, чем следующее значение для bucket_start.)

Если бы я собирался построить эту таблицу, я, вероятно, сделал бы это так.(Хотя я бы подумал, стоит ли мне называть это «календарем».)

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

Ограничение UNIQUE создает неявный индекс в PostgreSQL.

Этот запрос будет вставлять строки за один день (24 часа * 60 минут) за один раз.

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;

Вы можете заключить это в функцию, чтобы генерировать год за один раз.Вероятно, я бы попытался зафиксировать менее полумиллиона строк за один раз.

Для создания 20 миллионов строк для тестирования не потребуется слишком много времени и еще 20 миллионов строк "календарных" минут.Длинный обедМожет быть, полдень на солнце.

1 голос
/ 09 мая 2011

В построенных мной хранилищах данных я использовал отдельные измерения CALENDAR и TIME_OF_DAY. Первое измерение имеет гранулярность за 1 день, а второе - за 1 минуту.

В двух других случаях я заранее знал, что не требуется никаких отчетов при степени детализации менее 15 минут. В этом случае для простоты я использовал одно измерение CALENDAR с 96 записями в день.

До сих пор я использовал этот подход на складах Oracle, но я могу быть вовлечен в проект хранилища PostgreSQL этим летом.

...