
СЕКЦИОНИРОВАНИЕ
~~~~~~~~~~~~~~~

Создадим таблицу dates в базе данных db21

=> create database db21;
CREATE DATABASE

=> \c db21
You are now connected to database "db21" as user "postgres".

=> create table dates (ts timestamp);
CREATE TABLE

Наполним таблицу строками с минутным интервалом за все дни 2016 года

=> insert into dates 
=>    select t.ts from generate_series (
=>       '2016-01-01'::timestamp
=>      ,'2016-12-31'::timestamp
=>      ,'1 min'::interval
=>    ) as t (ts);
INSERT 0 525601

Этап 1. Подготовка.
Создаем четыре дочерние таблицы для каждого квартала и ограничения проверки

=> create table dates_2016q1 () inherits (dates);
CREATE TABLE

=> create table dates_2016q2 () inherits (dates);
CREATE TABLE

=> create table dates_2016q3 () inherits (dates);
CREATE TABLE

=> create table dates_2016q4 () inherits (dates);
CREATE TABLE

=> alter table dates_2016q1 add constraint check_ts check (
=>    ts >= '2016-01-01 00:00:00'::timestamp and 
=>    ts <  '2016-04-01 00:00:00'::timestamp
=> );
ALTER TABLE

=> alter table dates_2016q2 add constraint check_ts check (
=>    ts >= '2016-04-01 00:00:00'::timestamp and 
=>    ts <  '2016-07-01 00:00:00'::timestamp
=> );
ALTER TABLE

=> alter table dates_2016q3 add constraint check_ts check (
=>    ts >= '2016-07-01 00:00:00'::timestamp and 
=>    ts <  '2016-10-01 00:00:00'::timestamp
=> );
ALTER TABLE

=> alter table dates_2016q4 add constraint check_ts check (
=>    ts >= '2016-10-01 00:00:00'::timestamp and 
=>    ts <  '2017-01-01 00:00:00'::timestamp
=> );
ALTER TABLE

Этап 2. Запуск для новых секций
Создаем триггер на вставку записей в dates

=> create function dates_ins () returns trigger as $$
=> begin
=>    if new.ts >= '2016-01-01 00:00:00'::timestamp and 
=>       new.ts <  '2016-04-01 00:00:00'::timestamp
=>    then
=>       insert into dates_2016q1 select new.*;
=>    elsif new.ts >= '2016-04-01 00:00:00'::timestamp and 
=>          new.ts <  '2016-07-01 00:00:00'::timestamp
=>    then
=>       insert into dates_2016q2 select new.*;
=>    elsif new.ts >= '2016-07-01 00:00:00'::timestamp and 
=>          new.ts <  '2016-10-01 00:00:00'::timestamp
=>    then
=>       insert into dates_2016q3 select new.*;
=>    elsif new.ts >= '2016-10-01 00:00:00'::timestamp and 
=>          new.ts <  '2017-01-01 00:00:00'::timestamp
=>    then
=>       insert into dates_2016q4 select new.*;
=>    end if;
=>    return null;
=> end; $$ language plpgsql;
CREATE FUNCTION

=> create trigger dates_ins_trg
=>    before insert on dates
=>    for each row
=>    execute procedure dates_ins(); 
CREATE TRIGGER

Теперь секционирование работает для новых записей

=> insert into dates values ('2016-02-01');
INSERT 0 0

=> insert into dates values ('2016-05-01');
INSERT 0 0

=> insert into dates values ('2016-08-01');
INSERT 0 0

=> insert into dates values ('2016-11-01');
INSERT 0 0

=> select tableoid::regclass, count(*) from dates group by tableoid;
   tableoid   | count  
--------------+--------
 dates_2016q2 |      1
 dates_2016q3 |      1
 dates_2016q4 |      1
 dates_2016q1 |      1
 dates        | 525601
(5 rows)


Этап 3. Перенос исторических данных
Осталось перенести все строки из dates в соответствующие секции
Чтобы лишний раз не вызывать триггер, будем вставлять данные 
сразу в нужные дочерние таблицы.

=> with del as (
=>    delete from ONLY dates
=>     where ts >= '2016-01-01 00:00:00'::timestamp and 
=>           ts <  '2016-04-01 00:00:00'::timestamp
=>    returning *
=> )
=> insert into dates_2016q1 select * from del;
INSERT 0 131040

=> with del as (
=>    delete from ONLY dates
=>     where ts >= '2016-04-01 00:00:00'::timestamp and 
=>           ts <  '2016-07-01 00:00:00'::timestamp
=>    returning *
=> )
=> insert into dates_2016q2 select * from del;
INSERT 0 131040

=> with del as (
=>    delete from ONLY dates
=>     where ts >= '2016-07-01 00:00:00'::timestamp and 
=>           ts <  '2016-10-01 00:00:00'::timestamp
=>    returning *
=> )
=> insert into dates_2016q3 select * from del;
INSERT 0 132480

=> with del as (
=>    delete from ONLY dates
=>     where ts >= '2016-10-01 00:00:00'::timestamp and 
=>           ts <  '2017-01-01 00:00:00'::timestamp
=>    returning *
=> )
=> insert into dates_2016q4 select * from del;
INSERT 0 131041

Теперь все данные разложены по своим секциям

=> select tableoid::regclass, count(*) from dates group by tableoid;
   tableoid   | count  
--------------+--------
 dates_2016q2 | 131041
 dates_2016q3 | 132481
 dates_2016q4 | 131042
 dates_2016q1 | 131041
(4 rows)


А запросы обращаются только к нужным

=> explain (costs off) select * from dates where ts < '2016-05-09'::timestamp;
                                QUERY PLAN                                 
---------------------------------------------------------------------------
 Append
   ->  Seq Scan on dates
         Filter: (ts < '2016-05-09 00:00:00'::timestamp without time zone)
   ->  Seq Scan on dates_2016q1
         Filter: (ts < '2016-05-09 00:00:00'::timestamp without time zone)
   ->  Seq Scan on dates_2016q2
         Filter: (ts < '2016-05-09 00:00:00'::timestamp without time zone)
(7 rows)


=> \q
