Manter o valor do resumo é complicado - é fácil criar a possibilidade de deadlock seu programa.
Se você realmente precisa fazer isso, porque sabe que terá problemas de desempenho de outra forma (como nhunts em centenas ou mais), é melhor criar uma tabela de resumo separada para nhunts, algo como:
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);
O gatilho para caças:
- é executado para cada linha adicionada, removida e atualizada;
- adiciona uma linha
(id_h, nhunts) = (NEW.id_h, 1)
em cada inserção; - adiciona uma linha
(id_h, nhunts) = (OLD.id_h, -1)
em cada exclusão; - ambos os itens acima na atualização que altera
id_h
.
Como o gatilho só adicionará novas linhas, ele não bloqueará as linhas existentes e, portanto, não poderá travar.
Mas isso não é suficiente - conforme descrito acima, a tabela de resumo aumentará as linhas tão rápido ou mais rápido que a tabela de buscas, portanto, não é muito útil. Portanto, precisamos adicionar alguma maneira de mesclar as linhas existentes periodicamente - alguma maneira de alterar:
id_h nhunts
1 1
1 1
2 1
2 -1
1 1
1 -1
2 1
1 1
2 1
Para:
id_h nhunts
1 3
2 2
Isso não deve ser executado em cada invocação de gatilho, pois será bastante lento, mas pode ser executado aleatoriamente - por exemplo, a cada 1/1024 invocação aleatoriamente. Esta função usará a palavra-chave "pular bloqueado" para evitar tocar em linhas já bloqueadas, evitando um possível impasse.
Esse gatilho seria algo assim:
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();
O gatilho é executado rápido o suficiente no meu laptop para inserir 1 milhão de linhas na tabela de caças em 45s.
Esta visualização abaixo facilitará a extração de nhunts atuais do resumo. A consulta levará um pequeno número ou ms, mesmo que a tabela de caças esteja em bilhões:
create or replace view hunts_summary_view as
select id_h, sum(nhunts) as nhunts
from hunts_summary
group by id_h;