Таблицы календаря в PostgreSQL 9

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

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

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

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


person NJ.    schedule 28.04.2011    source источник


Ответы (3)


Календарные таблицы реализуют компромисс между пространством и временем. Благодаря большему объему памяти некоторые типы запросов выполняются быстрее, поскольку они могут использовать преимущества индексов. Они безопасны до тех пор, пока вы соблюдаете осторожность с ограничениями CHECK() и пока у вас есть административные процессы для обработки любых ограничений, которые не поддерживает ваша СУБД.

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

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 миллионов строк «календарных» минут не займет много времени. Долгий обед. Может быть, полдень на солнце.

person Mike Sherrill 'Cat Recall'    schedule 28.04.2011
comment
О каком именно преимуществе индексов вы говорите? - person Quassnoi; 29.04.2011

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

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

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

person Quassnoi    schedule 28.04.2011
comment
Да, но объединение результата generate_series(), возможно, с 20 миллионами строк, вероятно, приведет к снижению производительности в унитаз. Запросы к таблице календаря могут использовать преимущества индексов. - person Mike Sherrill 'Cat Recall'; 28.04.2011
comment
@Catcall: запросы к таблице календаря обычно предполагают левое соединение между всеми значениями в таблице календаря и некоторыми значениями в таблице фактов, так что для периодов без записей фактов возвращается NULL записей. Не могли бы вы предоставить пример запроса, который выиграет от замены generate_series фактической таблицей с данными? - person Quassnoi; 28.04.2011
comment
Интересный. Спасибо. Итак, идея состоит в том, что я мог бы создать три такие таблицы: дни, часы, минуты, и моя таблица набора данных будет иметь такие ключи, как «day_id», «hour_id», «minute_id», которые можно использовать вместе или независимо, в зависимости от разрешения. мой анализ? Если так, то это здорово. Если нет, то я что-то упускаю. - person NJ.; 28.04.2011
comment
@NJ: Почему, вы можете сгенерировать минутное разрешение прямо в generate_series. Пожалуйста, напишите запрос, который вы пытаетесь сделать, и я расскажу вам, как связать его с generate_series. - person Quassnoi; 28.04.2011
comment
@NJ обычно у вас есть только одна денормализованная таблица календаря и один ключ в таблице фактов. minute_id в данном случае, так как это степень детализации вашего календаря. Ваша таблица календаря будет иметь, например. столбец месяца, столбец дня, столбец минут. Чтобы получить все данные за день, выполните select ... from calendar c left join datatable d on c.id = d.minute_id where c.day='2011-04-28' . Чтобы получить данные за минуту, вы делаете select ... from calendar c left join datatable d on c.id = d.minute_id where c.minute='2011-04-28 04:10:00' - person nos; 28.04.2011

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

В двух других случаях я заранее знал, что отчеты не потребуются при детализации менее 15 минут. В этом случае для простоты я использовал одно измерение КАЛЕНДАРЯ с 96 записями в день.

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

person Olaf    schedule 09.05.2011