sql >> Databáze >  >> RDS >> PostgreSQL

Jak co nejtransparentněji migrovat existující tabulku Postgres na rozdělenou tabulku?

V Postgres 10 bylo představeno „Deklarativní rozdělení“, které vás může zbavit spousty práce, jako je generování spouštěčů nebo pravidel s obrovskými příkazy if/else přesměrováním na správnou tabulku. Postgres to nyní umí automaticky. Začněme s migrací:

  1. Přejmenujte starou tabulku a vytvořte novou rozdělenou tabulku

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

To by snad nemělo vyžadovat žádné vysvětlení. Stará tabulka je přejmenována (po migraci dat ji smažeme) a získáme hlavní tabulku pro náš oddíl, která je v podstatě stejná jako naše původní tabulka, ale bez indexů)

  1. Vytvořte funkci, která dokáže generovat nové oddíly, jak je potřebujeme:

    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;
    

To se bude hodit později.

  1. Vytvořte pohled, který v podstatě pouze deleguje naši hlavní tabulku:

    create or replace view myTable as select * from myTable_master;
    
  2. Vytvořte pravidlo, abychom po vložení do pravidla nejen aktualizovali rozdělenou tabulku, ale v případě potřeby také vytvořili nový oddíl:

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

Samozřejmě, pokud potřebujete také update a delete , potřebujete také pravidlo pro ty, které by měly být přímočaré.

  1. Ve skutečnosti migrujte starou tabulku:

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

Nyní je migrace tabulky kompletní, aniž by bylo potřeba vědět, kolik oddílů je potřeba, a také pohled myTable bude naprosto transparentní. Můžete jednoduše vkládat a vybírat z této tabulky jako dříve, ale můžete získat výhodu výkonu z rozdělení.

Všimněte si, že je potřeba pouze zobrazení, protože dělená tabulka nemůže mít spouštěče řádků. Pokud si vystačíte s voláním createPartitionIfNotExists ručně kdykoli je to potřeba z vašeho kódu, nepotřebujete zobrazení a všechna jeho pravidla. V tomto případě musíte během migrace přidat oddíly také ručně:

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. OBJEDNAT BY Alias ​​nefunguje

  2. Oprava Postgresql po upgradu na OSX 10.7 Lion

  3. Efektivní monitorování MySQL pomocí řídicích panelů SCUMM:Část 3

  4. Výkon SQL:WHERE vs. WHERE (ROW_NUMBER)