PostgreSQL
 sql >> Datenbank >  >> RDS >> PostgreSQL

Sie müssen einen Trigger erstellen, der einen Wert in einer Tabelle nach dem Einfügen erhöht

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;