Die Beibehaltung des zusammenfassenden Werts ist schwierig – es ist einfach, eine Möglichkeit für Deadlock zu schaffen Ihr Programm.
Wenn Sie dies wirklich tun müssen, weil Sie wissen, dass Sie sonst Leistungsprobleme haben werden (wie nhunts in Hunderten oder mehr), dann ist es besser, eine separate Übersichtstabelle für nhunts zu erstellen, etwa so:
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);
Der Auslöser für Jagden:
- wird für jede hinzugefügte, entfernte, aktualisierte Zeile ausgeführt;
- fügt eine Zeile
(id_h, nhunts) = (NEW.id_h, 1)
hinzu auf jeder Beilage; - fügt eine Zeile
(id_h, nhunts) = (OLD.id_h, -1)
hinzu bei jedem Löschen; - Beides oben bei einem Update, das
id_h
ändert .
Da der Trigger nur neue Zeilen hinzufügt, sperrt er keine bestehenden Zeilen und kann daher keine Deadlocks ausführen.
Aber das ist nicht genug - wie oben beschrieben, werden die Zeilen der Übersichtstabelle genauso schnell oder schneller wachsen als die Jagdtabelle, also ist es nicht sehr hilfreich. Also müssen wir eine Möglichkeit hinzufügen, bestehende Zeilen regelmäßig zusammenzuführen - eine Möglichkeit, dies zu ändern:
id_h nhunts
1 1
1 1
2 1
2 -1
1 1
1 -1
2 1
1 1
2 1
An:
id_h nhunts
1 3
2 2
Dies sollte nicht bei jedem Trigger-Aufruf ausgeführt werden, da es dann ziemlich langsam ist, aber es kann zufällig ausgeführt werden - zum Beispiel bei jedem 1/1024-ten Aufruf zufällig. Diese Funktion verwendet das Schlüsselwort "skip locked", um zu vermeiden, dass bereits gesperrte Zeilen berührt werden, wodurch ein andernfalls möglicher Deadlock vermieden wird.
Ein solcher Trigger würde in etwa so aussehen:
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();
Der Trigger läuft auf meinem Laptop schnell genug, um in 45 Sekunden 1 Million Zeilen in die Suchtabelle einzufügen.
Diese Ansicht unten macht es einfach, aktuelle nhunts aus der Zusammenfassung zu extrahieren. Die Abfrage dauert eine kleine Zahl oder Millisekunden, selbst wenn die Suchtabelle in Milliardenhöhe liegt:
create or replace view hunts_summary_view as
select id_h, sum(nhunts) as nhunts
from hunts_summary
group by id_h;