PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Besoin de créer un déclencheur qui incrémente une valeur dans une table après l'insertion

Maintenir la valeur du résumé est délicat - il est facile de créer une possibilité de deadlock votre programme.

Si vous devez vraiment faire cela, parce que vous savez que vous aurez des problèmes de performances autrement (comme des nhunts par centaines ou plus), alors il est préférable de créer un tableau récapitulatif séparé pour les nhunts, quelque chose comme :

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

Le déclencheur des chasses :

  • s'exécute pour chaque ligne ajoutée, supprimée ou mise à jour ;
  • ajoute une ligne (id_h, nhunts) = (NEW.id_h, 1) sur chaque encart ;
  • ajoute une ligne (id_h, nhunts) = (OLD.id_h, -1) à chaque suppression ;
  • les deux éléments ci-dessus sur la mise à jour qui modifie id_h .

Comme le déclencheur n'ajoutera que de nouvelles lignes, il ne verrouille pas les lignes existantes et ne peut donc pas bloquer.

Mais cela ne suffit pas - comme décrit ci-dessus, le tableau récapitulatif augmentera les lignes aussi rapidement ou plus rapidement que le tableau des chasses, ce n'est donc pas très utile. Nous devons donc ajouter un moyen de fusionner périodiquement les lignes existantes - un moyen de changer :

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

À :

id_h nhunts
1    3
2    2

Cela ne devrait pas s'exécuter à chaque invocation de déclencheur, car il sera alors assez lent, mais il peut s'exécuter de manière aléatoire - par exemple chaque 1/1024e invocation au hasard. Cette fonction utilisera le mot-clé "sauter verrouillé" pour éviter de toucher des lignes déjà verrouillées, évitant ainsi un blocage possible.

Un tel déclencheur ressemblerait à ceci :

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

Le déclencheur s'exécute assez rapidement sur mon ordinateur portable pour insérer 1 million de lignes dans la table des chasses en 45 secondes.

Cette vue ci-dessous facilitera l'extraction des nhunts actuels à partir du résumé. L'interroger prendra un petit nombre ou ms même si la table des chasses sera en milliards :

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