I Postgres 10 blev "Declarative Partitioning" introduceret, som kan aflaste dig for en del arbejde, såsom at generere triggere eller regler med enorme if/else-sætninger, der omdirigerer til den korrekte tabel. Postgres kan gøre dette automatisk nu. Lad os starte med migreringen:
-
Omdøb den gamle tabel og opret en ny opdelt tabel
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);
Dette burde næppe kræve nogen forklaring. Den gamle tabel omdøbes (efter datamigrering sletter vi den), og vi får en mastertabel til vores partition, som stort set er den samme som vores oprindelige tabel, men uden indekser)
-
Opret en funktion, der kan generere nye partitioner, efterhånden som vi har brug for 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;
Dette vil være nyttigt senere.
-
Opret en visning, der stort set blot uddelegerer til vores mastertabel:
create or replace view myTable as select * from myTable_master;
-
Opret regel, så når vi indsætter i reglen, vil vi ikke bare opdatere den partitionerede tabel, men også oprette en ny partition, hvis det er nødvendigt:
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) );
Selvfølgelig, hvis du også har brug for update
og delete
, du har også brug for en regel for dem, der skal være ligetil.
-
Migrerer faktisk den gamle tabel:
-- 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 er migreringen af tabellen komplet, uden at der var behov for at vide, hvor mange partitioner der er nødvendige, og også visningen myTable
vil være fuldstændig gennemsigtig. Du kan nemt indsætte og vælge fra den tabel som før, men du kan muligvis få fordelene ved at partitionere.
Bemærk, at visningen kun er nødvendig, fordi en opdelt tabel ikke kan have rækkeudløsere. Hvis du kan klare dig med at kalde createPartitionIfNotExists
manuelt, når det er nødvendigt fra din kode, behøver du ikke visningen og alle dens regler. I dette tilfælde skal du tilføje partitionerne også manuelt 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
$$;