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

Skal oprette en trigger, der øger en værdi i en tabel efter indsættelse

Det er vanskeligt at vedligeholde oversigtsværdien - det er nemt at skabe en mulighed for at deadlock dit program.

Hvis du virkelig er nødt til at gøre dette, fordi du ved, at du ellers vil have præstationsproblemer (som nhunts i hundredvis eller mere), så er det bedre at oprette en separat oversigtstabel for nhunts, sådan som:

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

Udløseren for jagter:

  • kører for hver tilføjet, fjernet, opdateret række;
  • tilføjer en række (id_h, nhunts) = (NEW.id_h, 1) på hver indsats;
  • tilføjer en række (id_h, nhunts) = (OLD.id_h, -1) ved hver sletning;
  • begge ovenstående ved opdatering, der ændrer id_h .

Da udløseren kun tilføjer nye rækker, låser den ikke eksisterende rækker, og den kan derfor ikke låse fast.

Men dette er ikke nok - som beskrevet ovenfor vil oversigtstabellen vokse rækker lige så hurtigt eller hurtigere end jagttabellen, så det er ikke særlig nyttigt. Så vi er nødt til at tilføje en måde at flette eksisterende rækker på med jævne mellemrum - en måde at ændre på:

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

Til:

id_h nhunts
1    3
2    2

Dette bør ikke køre på hver trigger invokation, da det så vil være ret langsomt, men det kan køre tilfældigt - for eksempel hver 1/1024. invocation tilfældigt. Denne funktion vil bruge nøgleordet "spring over låst" for at undgå at røre ved allerede låste rækker og undgå ellers mulig dødvande.

En sådan trigger ville se sådan ud:

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

Udløseren kører hurtigt nok på min bærbare computer til at indsætte 1M rækker til jagttabellen på 45s.

Denne visning nedenfor gør det nemt at udtrække aktuelle nhunts fra oversigten. Forespørgsel efter det vil tage et lille tal eller ms, selvom jagttabellen vil være i milliarder:

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



  1. Nulstil pg_stat_statements med Google Cloud SQL

  2. Heroku pg:push psql:FATAL:adgangskodegodkendelse mislykkedes for brugeren

  3. Langsomt kørende Postgres-forespørgsel

  4. Konstruktion af en midlertidig tabel i Oracle SQL