sql >> Database >  >> RDS >> PostgreSQL

Moet een trigger maken die een waarde in een tabel verhoogt na invoeging

Het behouden van de samenvattingswaarde is lastig - het is gemakkelijk om een ​​mogelijkheid te creëren om deadlock uw programma.

Als je dit echt moet doen, omdat je weet dat je anders prestatieproblemen zult hebben (zoals nhunts in honderden of meer), dan is het beter om een ​​aparte overzichtstabel te maken voor nhunts, zoiets als:

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

De trigger voor jachten:

  • wordt uitgevoerd voor elke toegevoegde, verwijderde, bijgewerkte rij;
  • voegt een rij toe (id_h, nhunts) = (NEW.id_h, 1) op elke insert;
  • voegt een rij toe (id_h, nhunts) = (OLD.id_h, -1) bij elke verwijdering;
  • beide van bovenstaande bij update die id_h verandert .

Omdat de trigger alleen nieuwe rijen zal toevoegen, worden bestaande rijen niet vergrendeld en kan daarom niet worden vastgezet.

Maar dit is niet genoeg - zoals hierboven beschreven, zal de samenvattende tabel rijen net zo snel of sneller laten groeien dan de hunt-tabel, dus het is niet erg nuttig. We moeten dus een manier toevoegen om bestaande rijen periodiek samen te voegen - een manier om te veranderen:

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

Aan:

id_h nhunts
1    3
2    2

Dit zou niet bij elke trigger-aanroep moeten worden uitgevoerd, omdat het dan vrij langzaam zal zijn, maar het kan willekeurig worden uitgevoerd - bijvoorbeeld elke 1/1024e aanroep willekeurig. Deze functie gebruikt het trefwoord "vergrendeld overslaan" om te voorkomen dat reeds vergrendelde rijen worden aangeraakt, waardoor een andere mogelijke impasse wordt vermeden.

Zo'n trigger ziet er ongeveer zo uit:

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

De trigger werkt snel genoeg op mijn laptop om 1M-rijen in te voegen om in 45s op tafel te jagen.

Deze weergave hieronder maakt het gemakkelijk om huidige nhunts uit het overzicht te halen. Het opvragen ervan duurt een klein aantal of ms, zelfs als de hunts-tabel in miljarden zal zijn:

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



  1. pgAdmin-alternatieven - GUI ClusterControl voor PostgreSQL-databasebeheer

  2. Voorbeelden van het converteren van 'date' naar 'datetime' in SQL Server (T-SQL)

  3. Wat zijn de kosten van het indexeren van meerdere db-kolommen?

  4. Rails Octopus Gem - Verbindingsgedrag Master-Slave Replicatie wanneer slave down is