2013-04-17 2 views
3

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

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

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

CREATE TABLE measurement_y2013w01 (
CHECK (logdate >= DATE '2013-01-07' AND logdate < DATE '2013-01-14') 
    ) INHERITS (measurement); 

    CREATE TABLE measurement_y2006w02 (
CHECK (logdate >= DATE '2013-01-14' AND logdate < DATE '2013-01-21') 
    ) INHERITS (measurement); 

... 

Но я хочу, чтобы это было сделано автоматически. Я не хочу создавать раздел на каждую неделю один за другим.

Мое правило для присвоения имени было бы yYYYYWWW для обозначения раздела или начала datadYYYYMMDD.

Я думал, что в проверке разделов при установке использовать что-то вроде этого:

SELECT 
    nmsp_parent.nspname AS parent_schema, 
    parent.relname  AS parent, 
    nmsp_child.nspname AS child, 
    child.relname  AS child_schema 
FROM pg_inherits 
    JOIN pg_class parent  ON pg_inherits.inhparent = parent.oid 
    JOIN pg_class child   ON pg_inherits.inhrelid = child.oid 
    JOIN pg_namespace nmsp_parent ON nmsp_parent.oid = parent.relnamespace 
    JOIN pg_namespace nmsp_child ON nmsp_child.oid = child.relnamespace 

и если раздел не существует создать его перед вставкой, но это швы много неэффективно, учитывая количество вставленных записей ,

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

Есть ли более эффективное решение для этого, которое я пропускаю, которое используется, например, для ежемесячных проверок?

+0

Вы будете иметь внешнее удаление процесса или перемещение старых разделов не так ли? Если да, то в чем проблема с созданием разделов? –

+0

Возможно, вам стоит взглянуть на pg_partman, который может автоматизировать для вас много вещей, связанных с секционированием: https://github.com/keithf4/pg_partman/blob/master/doc/pg_partman.md –

ответ

2

Функция Функция date_trunc позволяет округлить значение данных до первого дня недели. Для раздела имен можно использовать год и номер недели в году YYWW:

CREATE TABLE measurement_1301 (
    CHECK (date_trunc('week', logdate)::date = DATE '2013-01-07')) 
    INHERITS (measurement); 

CREATE TABLE measurement_1302 (
    CHECK (date_trunc('week', logdate)::date = DATE '2013-01-14')) 
    INHERITS (measurement); 

CREATE TABLE measurement_1303 (
    CHECK (date_trunc('week', logdate)::date = DATE '2013-01-21')) 
    INHERITS (measurement); 

-- Default partition: 
CREATE TABLE measurement_default() INHERITS (measurement); 

Для имен разделов использования поколения to_char(logdate::date, 'YYWW'), и если вы, если вы предпочитаете yYYYYwWW: to_char(logdate::date, '"y"YYYY"w"WW')

И проверить существующий разделы, вы можете использовать очень простой запрос:

SELECT relname FROM pg_class 
WHERE relname ~ '^measurement_[0-9]{4}$' 
ORDER BY RIGHT(relname,4) DESC 

Триггер маршрутизации данных в соответствующие части и вернуться к умолчанию, если для данной недели нет раздела.

CREATE OR REPLACE FUNCTION measurement_insert_trigger() 
RETURNS TRIGGER AS $$ 
BEGIN 

    IF to_char(NEW.logdate::date, 'YYWW') = '1301' THEN 
     INSERT INTO measurement_1301 VALUES (NEW.*); 
    ELSIF to_char(NEW.logdate::date, 'YYWW') = '1302' THEN 
     INSERT INTO measurement_1302 VALUES (NEW.*); 
    ELSIF to_char(NEW.logdate::date, 'YYWW') = '1303' THEN 
     INSERT INTO measurement_1303 VALUES (NEW.*); 
    -- ... 
    ELSE 
     INSERT INTO measurement_default VALUES (NEW.*); 
    END IF; 
    RETURN NULL; 
END; 
$$ LANGUAGE plpgsql; 

CREATE TRIGGER measurement_insert_tr BEFORE INSERT ON measurement 
FOR EACH ROW EXECUTE PROCEDURE measurement_insert_trigger() 

У вас будет возможность заранее создать все разделы. Или вы можете использовать раздел по умолчанию и время от времени переразделять хранящиеся там данные, создавать новые разделы и настраивать триггер insert.

PS вы можете найти скрипты для разделения раствора на основе триггеров здесь http://hg.nowitworks.eu/postgresql-triggers-and-partitions

+0

+1, поскольку это было полезно для большинство шагов. Все еще не отвечает полностью основную часть создания раздела, если они не существуют. – RGPT

4

Итак, давайте создадим себе функцию для обработки этого!

CREATE OR REPLACE FUNCTION create_partition_measurement(DATE, DATE) 
returns void AS $$ 
DECLARE 
create_query text; 
BEGIN 
    FOR create_query IN SELECT 

     'CREATE TABLE measurement_' || TO_CHAR(d, 'YYYY_WW') || ' (
     CHECK (EXTRACT(YEAR FROM logdate) = EXTRACT(YEAR FROM TIMESTAMP ''' || d || ''') AND EXTRACT(WEEK FROM logdate) = EXTRACT(WEEK FROM TIMESTAMP ''' || d || ''')) 
     ) INHERITS (measurement);' 

     FROM generate_series($1, $2, '1 week') AS d LOOP 

     EXECUTE create_query; 

    END LOOP; 
END; 
$$ 
language plpgsql; 

С этим теперь вы можете назвать что-то вроде

SELECT create_partition_measurement ('2015/02/08','2015/03/01'); 

и создали свои разделы. Первый шаг к автоматизации.

Я испытал все это в моей базе данных с помощью следующей тестовой таблицы:

CREATE TABLE measurement (id INT NOT NULL PRIMARY KEY, id_user INT NOT NULL, logdate TIMESTAMP NOT NULL); 

После создания разделов с помощью функции выше, я был в состоянии:

  • Вставка данных в правильном раздел;
  • Ошибка при попытке вставить данные из одной недели в раздел другой недели;
  • Автоматическое создание раздела в течение нескольких недель;
  • Получите ошибку, если я попытаюсь создать раздел на неделю, где он уже существует.

Это должно быть достаточно =)

Теперь об автоматизации процесса создания. Я использую простой скрипт cron для вызова этой функции для меня каждый месяц и несколько контрольных скриптов, чтобы убедиться, что все работает так, как должно. хрон выглядит следующим образом:

0 0 1 * * /var/lib/postgresql/create_partitions.sh 

И сценарий будет запускать команду, используя текущую дату и текущую дату + 1 месяц. Это выглядит примерно так:

startDate=`date "+%Y/%m/%d"` 
endDate=`date -u -d "+1 month -$(date +%d) days" "+%Y/%m/%d" 
psql -U "$dbUser" -w -c "SELECT create_partition_measurement('$startDate','$endDate');" 

Если вам необходимо включить индексы, первичные ключи, FKs в таблице, или помочь с триггерами, чтобы сделать всю эту работу, просто дайте мне знать.

0

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

https://github.com/bitdivine/pg_day_partitions

Смежные вопросы