В Postgres 10 беше въведено „Декларативно разделяне“, което може да ви освободи от голяма част от работа, като генериране на тригери или правила с огромни оператори if/else, пренасочващи към правилната таблица. Postgres може да направи това автоматично сега. Да започнем с миграцията:
-
Преименувайте старата таблица и създайте нова разделена таблица
alter table myTable rename to myTable_old; create table myTable_master( forDate date not null, key2 int not null, value int not null ) partition by range (forDate);
Това едва ли трябва да изисква обяснение. Старата таблица се преименува (след миграцията на данни ще я изтрием) и получаваме главна таблица за нашия дял, която по същество е същата като оригиналната ни таблица, но без индекси)
-
Създайте функция, която може да генерира нови дялове, както са ни необходими:
create function createPartitionIfNotExists(forDate date) returns void as $body$ declare monthStart date := date_trunc('month', forDate); declare monthEndExclusive date := monthStart + interval '1 month'; -- We infer the name of the table from the date that it should contain -- E.g. a date in June 2005 should be int the table mytable_200506: declare tableName text := 'mytable_' || to_char(forDate, 'YYYYmm'); begin -- Check if the table we need for the supplied date exists. -- If it does not exist...: if to_regclass(tableName) is null then -- Generate a new table that acts as a partition for mytable: execute format('create table %I partition of myTable_master for values from (%L) to (%L)', tableName, monthStart, monthEndExclusive); -- Unfortunatelly Postgres forces us to define index for each table individually: execute format('create unique index on %I (forDate, key2)', tableName); end if; end; $body$ language plpgsql;
Това ще ви бъде полезно по-късно.
-
Създайте изглед, който просто делегира на нашата главна таблица:
create or replace view myTable as select * from myTable_master;
-
Създайте правило, така че когато вмъкнем в правилото, не само ще актуализираме разделената таблица, но и ще създадем нов дял, ако е необходимо:
create or replace rule autoCall_createPartitionIfNotExists as on insert to myTable do instead ( select createPartitionIfNotExists(NEW.forDate); insert into myTable_master (forDate, key2, value) values (NEW.forDate, NEW.key2, NEW.value) );
Разбира се, ако имате нужда и от update
и delete
, имате нужда и от правило за тези, които трябва да са прави.
-
Всъщност мигрирайте старата таблица:
-- Finally copy the data to our new partitioned table insert into myTable (forDate, key2, value) select * from myTable_old; -- And get rid of the old table drop table myTable_old;
Сега миграцията на таблицата е завършена, без да е имало нужда да се знае колко дяла са необходими, както и изгледът myTable
ще бъде абсолютно прозрачен. Можете лесно да вмъквате и избирате от тази таблица, както преди, но може да получите полза от производителността от разделянето.
Имайте предвид, че изгледът е необходим само, тъй като разделена таблица не може да има задействания на редове. Ако можете да се разберете с извикването на createPartitionIfNotExists
ръчно, когато е необходимо от вашия код, нямате нужда от изгледа и всички негови правила. В този случай трябва да добавите дяловете ръчно по време на миграция:
do
$$
declare rec record;
begin
-- Loop through all months that exist so far...
for rec in select distinct date_trunc('month', forDate)::date yearmonth from myTable_old loop
-- ... and create a partition for them
perform createPartitionIfNotExists(rec.yearmonth);
end loop;
end
$$;