sql >> Databasteknik >  >> RDS >> PostgreSQL

Behöver skapa en utlösare som ökar ett värde i en tabell efter infogning

Det är svårt att upprätthålla ett sammanfattningsvärde – det är lätt att skapa en möjlighet att deadlock ditt program.

Om du verkligen måste göra detta, eftersom du vet att du annars kommer att ha prestandaproblem (som nhunts i hundratals eller mer), så är det bättre att skapa en separat sammanfattningstabell för nhunts, något 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);

Utlösaren för jakter:

  • körs för varje tillagd, borttagen, uppdaterad rad;
  • lägger till en rad (id_h, nhunts) = (NEW.id_h, 1) på varje inlägg;
  • lägger till en rad (id_h, nhunts) = (OLD.id_h, -1) vid varje radering;
  • båda ovanstående vid uppdatering som ändrar id_h .

Eftersom utlösaren bara lägger till nya rader låser den inte befintliga rader och kan därför inte låsa fast.

Men detta räcker inte - som beskrivits ovan kommer sammanfattningstabellen att växa rader lika snabbt eller snabbare än jakttabellen, så det är inte särskilt användbart. Så vi måste lägga till något sätt att slå samman befintliga rader med jämna mellanrum - något sätt att ändra:

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

Till:

id_h nhunts
1    3
2    2

Detta bör inte köras på varje triggeranrop, eftersom det då blir ganska långsamt, men det kan köras slumpmässigt - till exempel var 1/1024:e anrop slumpmässigt. Den här funktionen kommer att använda nyckelordet "hoppa över låst" för att undvika att vidröra redan låsta rader och undvika annars möjligt dödläge.

En sådan trigger skulle se ut ungefär så här:

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

Utlösaren går tillräckligt snabbt på min bärbara dator för att infoga 1M rader till jakttabellen på 45s.

Den här vyn nedan gör det enkelt att extrahera aktuella nhunts från sammanfattningen. Att fråga efter det kommer att ta ett litet antal eller ms även om jakttabellen kommer att vara i miljarder:

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



  1. 5 säkerhetsfördelar med molnbaserade databasövervakningslösningar

  2. Hur försöker man om transaktionen efter ett dödläge med Doctrine?

  3. APEX:Ladda ner BLOB från den tillfälliga tabellen

  4. Funktion i SQL Server 2008 som liknar GREATEST i mysql?