sql >> Database >  >> RDS >> PostgreSQL

Hoe een bestaande Postgres-tabel zo transparant mogelijk naar een gepartitioneerde tabel migreren?

In Postgres 10 is "Declarative Partitioning" geïntroduceerd, wat u veel werk kan ontnemen, zoals het genereren van triggers of regels met enorme if/else-statements die naar de juiste tabel doorverwijzen. Postgres kan dit nu automatisch doen. Laten we beginnen met de migratie:

  1. Hernoem de oude tabel en maak een nieuwe gepartitioneerde 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);
    

Dit behoeft nauwelijks enige uitleg. De oude tabel is hernoemd (na gegevensmigratie zullen we deze verwijderen) en we krijgen een hoofdtabel voor onze partitie die in principe hetzelfde is als onze originele tabel, maar zonder indexen)

  1. Maak een functie die nieuwe partities kan genereren als we ze nodig hebben:

    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;
    

Dit komt later van pas.

  1. Creëer een weergave die eigenlijk alleen naar onze hoofdtabel wordt gedelegeerd:

    create or replace view myTable as select * from myTable_master;
    
  2. Maak een regel zodat we bij het invoegen in de regel niet alleen de gepartitioneerde tabel bijwerken, maar indien nodig ook een nieuwe partitie maken:

    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)
        );
    

Natuurlijk, als je ook update nodig hebt en delete , je hebt ook een regel nodig voor degenen die eenvoudig moeten zijn.

  1. De oude tabel daadwerkelijk migreren:

    -- 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 is de migratie van de tabel voltooid zonder dat het nodig was om te weten hoeveel partities er nodig zijn en ook de weergave myTable volledig transparant zal zijn. U kunt eenvoudig die tabel invoegen en selecteren zoals voorheen, maar u kunt het prestatievoordeel halen uit partitionering.

Houd er rekening mee dat de weergave alleen nodig is, omdat een gepartitioneerde tabel geen rijtriggers kan hebben. Als je kunt opschieten met het bellen van createPartitionIfNotExists handmatig wanneer nodig vanuit uw code, u hebt de weergave en al zijn regels niet nodig. In dit geval moet u de partities ook handmatig toevoegen tijdens de migratie:

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
$$;


  1. LINQ to Entities herkent de methode 'System.String ToString()' niet, en deze methode kan niet worden vertaald in een winkeluitdrukking

  2. Converteer SQLite naar JSON

  3. SQL Server-triggers:DML-triggers

  4. MySQL Group_Concat() versus T-SQL String_Agg()