PostgreSQL
 sql >> Database >  >> RDS >> PostgreSQL

È necessario creare un trigger che incrementi un valore in una tabella dopo l'inserimento

Mantenere il valore di riepilogo è complicato:è facile creare la possibilità di deadlock il tuo programma.

Se devi davvero farlo, perché sai che altrimenti avrai problemi di prestazioni (come nhunt in centinaia o più), allora è meglio creare una tabella di riepilogo separata per nhunt, qualcosa del tipo:

CREATE TABLE hunts_summary
(
    id_hs bigserial primary key,
    id_h integer NOT NULL,
    nhunts integer NOT NULL
);
CREATE INDEX hunts_summary_id_h_idx on hunts_summary(id_h);

L'innesco delle cacce:

  • viene eseguito per ogni riga aggiunta, rimossa o aggiornata;
  • aggiunge una riga (id_h, nhunts) = (NEW.id_h, 1) su ogni inserto;
  • aggiunge una riga (id_h, nhunts) = (OLD.id_h, -1) ad ogni eliminazione;
  • entrambi i precedenti sull'aggiornamento che cambia id_h .

Poiché l'attivatore aggiungerà solo nuove righe, non blocca le righe esistenti e quindi non può bloccarsi.

Ma questo non è abbastanza:come descritto sopra, la tabella di riepilogo aumenterà le righe più velocemente o più velocemente della tabella di caccia, quindi non è molto utile. Quindi abbiamo bisogno di aggiungere un modo per unire periodicamente le righe esistenti - un modo per cambiare:

id_h nhunts
1    1
1    1
2    1
2    -1
1    1
1    -1
2    1
1    1
2    1

A:

id_h nhunts
1    3
2    2

Questo non dovrebbe essere eseguito su ogni invocazione del trigger, poiché sarà quindi piuttosto lento, ma può essere eseguito in modo casuale, ad esempio ogni 1/1024 di invocazione casuale. Questa funzione utilizzerà la parola chiave "salta bloccata" per evitare di toccare le righe già bloccate, evitando altrimenti possibili deadlock.

Tale trigger sarebbe simile a questo:

create or replace function hunts_maintain() returns trigger
as $hunts_maintain$
        begin
                if (tg_op = 'INSERT') then
                        insert into hunts_summary(id_h, nhunts)
                                values (NEW.id_h, 1);
                elsif (tg_op = 'DELETE') then
                        insert into hunts_summary(id_h, nhunts)
                                values (OLD.id_h, -1);
                elsif (tg_op = 'UPDATE' and NEW.id_h!=OLD.id_h) then
                        insert into hunts_summary(id_h, nhunts)
                                values (OLD.id_h, -1), (NEW.id_h, 1);
                end if;

                if (random()*1024 < 1) then
                        with deleted_ids as (
                                select id_hs from hunts_summary for update skip locked
                        ),
                        deleted_nhunts as (
                                delete from hunts_summary where id_hs in (select id_hs from deleted_ids) returning id_h, nhunts
                        )
                        insert into hunts_summary (id_h, nhunts) select id_h, sum(nhunts) from deleted_nhunts group by id_h;
                end if;

                return NEW;
        end;
$hunts_maintain$ language plpgsql;

create trigger hunts_maintain
        after insert or update or delete on hunts
        for each row execute procedure hunts_maintain();

Il trigger funziona abbastanza velocemente sul mio laptop da inserire 1 milione di righe nella tabella di caccia in 45 secondi.

Questa vista di seguito semplificherà l'estrazione dei nhunt correnti dal riepilogo. La query richiederà un piccolo numero o ms anche se la tabella di ricerca sarà in miliardi:

create or replace view hunts_summary_view as
        select id_h, sum(nhunts) as nhunts
        from hunts_summary
        group by id_h;