Udržování souhrnné hodnoty je složité – je snadné vytvořit možnost deadlock váš program.
Pokud to opravdu musíte udělat, protože víte, že jinak budete mít problémy s výkonem (jako nhunty ve stovkách nebo více), pak je lepší vytvořit samostatnou souhrnnou tabulku pro nhunty, něco jako:
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);
Spouštěč honů:
- běží pro každý přidaný, odstraněný, aktualizovaný řádek;
- přidá řádek
(id_h, nhunts) = (NEW.id_h, 1)
na každé vložce; - přidá řádek
(id_h, nhunts) = (OLD.id_h, -1)
při každém smazání; - obě výše uvedené při aktualizaci, která mění
id_h
.
Protože spouštěč bude pouze přidávat nové řádky, nezamyká existující řádky, a proto nemůže uváznout.
Ale to nestačí - jak je popsáno výše, souhrnná tabulka bude růst řádků stejně rychle nebo rychleji než tabulka lovů, takže to není příliš užitečné. Potřebujeme tedy přidat nějaký způsob, jak pravidelně slučovat existující řádky – nějaký způsob, jak to změnit:
id_h nhunts
1 1
1 1
2 1
2 -1
1 1
1 -1
2 1
1 1
2 1
Komu:
id_h nhunts
1 3
2 2
Toto by se nemělo spouštět při každém vyvolání spouštěče, protože pak bude poměrně pomalé, ale může se spustit náhodně - například každé 1/1024 náhodné vyvolání. Tato funkce použije klíčové slovo "přeskočit uzamčeno", aby se zabránilo dotyku již zamčených řádků, čímž se zabrání možnému uváznutí.
Takový spouštěč by vypadal asi takto:
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();
Spoušť běží na mém notebooku dostatečně rychle, abych vložil 1 milion řádků do tabulky lovu za 45 s.
Toto zobrazení níže usnadní extrahování aktuálních nhuntů ze souhrnu. Dotazování bude trvat malé číslo nebo ms, i když tabulka lovů bude v miliardách:
create or replace view hunts_summary_view as
select id_h, sum(nhunts) as nhunts
from hunts_summary
group by id_h;