I Postgres 10 introducerades "Deklarativ partitionering", vilket kan avlasta dig från en hel del arbete som att generera triggers eller regler med enorma if/else-satser som omdirigerar till rätt tabell. Postgres kan göra detta automatiskt nu. Låt oss börja med migreringen:
-
Byt namn på den gamla tabellen och skapa en ny partitionerad tabell
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);
Detta torde knappast kräva någon förklaring. Den gamla tabellen döps om (efter datamigrering tar vi bort den) och vi får en huvudtabell för vår partition som i princip är densamma som vår ursprungliga tabell, men utan index)
-
Skapa en funktion som kan generera nya partitioner när vi behöver dem:
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;
Detta kommer väl till pass senare.
-
Skapa en vy som i princip bara delegerar till vårt huvudbord:
create or replace view myTable as select * from myTable_master;
-
Skapa regel så att när vi infogar i regeln kommer vi inte bara att uppdatera den partitionerade tabellen, utan också skapa en ny partition om det behövs:
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) );
Naturligtvis, om du också behöver update
och delete
, du behöver också en regel för dem som ska vara rättfram.
-
Migrera faktiskt den gamla tabellen:
-- 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;
Nu är migreringen av tabellen klar utan att det behövdes veta hur många partitioner som behövs och även vyn myTable
kommer att vara helt transparent. Du kan enkelt infoga och välja från den tabellen som tidigare, men du kan få prestandafördelar med partitionering.
Observera att vyn endast behövs eftersom en partitionerad tabell inte kan ha radutlösare. Om du kan komma överens med att ringa createPartitionIfNotExists
manuellt närhelst det behövs från din kod, du behöver inte vyn och alla dess regler. I det här fallet måste du lägga till partitionerna också manuellt under migreringen:
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
$$;