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;