PostgreSQL
 sql >> Base de Dados >  >> RDS >> PostgreSQL

Precisa criar um gatilho que incremente um valor em uma tabela após a inserção


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;