it-swarm.com.de

In einer Trigger-Funktion können Sie ermitteln, welche Felder aktualisiert werden

Ist das möglich? Ich möchte herausfinden, welche Spalten in der Anforderung UPDATE angegeben wurden, unabhängig davon, ob der neue Wert, der gesendet wird, bereits in der Datenbank gespeichert ist oder nicht.

Der Grund, warum ich dies tun möchte, ist, dass wir eine Tabelle haben, die Aktualisierungen von mehreren Quellen empfangen kann. Bisher haben wir nicht aufgezeichnet, von welcher Quelle das Update stammt. In der Tabelle wird nun gespeichert, von welcher Quelle die letzte Aktualisierung durchgeführt wurde. Wir können einige der Quellen ändern, um eine Kennung zu senden, aber das ist nicht für alle Fälle eine Option. Daher möchte ich in der Lage sein, zu erkennen, wenn eine UPDATE-Anforderung keinen Bezeichner hat, sodass ich einen Standardwert einsetzen kann.

25
EvilAmarant7x

Wenn eine "Quelle" keinen "Bezeichner" sendet, bleibt die Spalte unverändert. Dann können Sie nicht feststellen, ob die aktuelle UPDATE von derselben Quelle wie die letzte oder von einer Quelle stammt, die die Spalte überhaupt nicht geändert hat. Mit anderen Worten: Das funktioniert nicht richtig.

Wenn die "Quelle" durch eine Sitzungsinformationsfunktion identifiziert werden kann, können Sie damit arbeiten. Mögen:

NEW.column = session_user;

Bedingungslos für jedes Update.

Allgemeine lösung

Ich habe einen Weg gefunden, um das ursprüngliche Problem zu lösen. Die Spalte wird auf einen Standardwert in any update gesetzt, wobei die Spalte not updated ist (nicht in der Liste SET der UPDATE).

Schlüsselelement ist ein spaltenweiser Trigger , der in PostgreSQL 9.0 eingeführt wurde - ein spaltenspezifischer Trigger, der die UPDATE OFcolumn_name -Klausel verwendet.

Der Trigger wird nur ausgelöst, wenn mindestens eine der aufgelisteten Spalten als Ziel des Befehls UPDATE angegeben ist.

Das ist die einzige einfache Möglichkeit, um zu unterscheiden, ob eine Spalte mit einem neuen Wert aktualisiert wurde, der mit dem alten identisch ist, oder nicht.

Ein könnte parst auch den von current_query() zurückgegebenen Text. Aber das scheint schwierig und unzuverlässig zu sein.

Funktionen auslösen

Ich gehe von einer Spalte col defined NOT NULL aus.

Schritt 1: col auf NULL setzen, falls unverändert:

CREATE OR REPLACE FUNCTION trg_tbl_upbef_step1()
  RETURNS trigger AS
$func$
BEGIN
   IF OLD.col = NEW.col THEN
      NEW.col := NULL;      -- "impossible" value
   END IF;

   RETURN NEW;
END
$func$  LANGUAGE plpgsql;

Schritt 2: Zum alten Wert zurückkehren. Trigger wird nur ausgelöst, wenn der Wert tatsächlich aktualisiert wurde (siehe unten):

CREATE OR REPLACE FUNCTION trg_tbl_upbef_step2()
  RETURNS trigger AS
$func$
BEGIN
   IF NEW.col IS NULL THEN
      NEW.col := OLD.col;
   END IF;

   RETURN NEW;
END
$func$  LANGUAGE plpgsql;

Schritt 3: Jetzt können wir das fehlende Update identifizieren und stattdessen einen Standardwert festlegen:

CREATE OR REPLACE FUNCTION trg_tbl_upbef_step3()
  RETURNS trigger AS
$func$
BEGIN
   IF NEW.col IS NULL THEN
      NEW.col := 'default value';
   END IF;

   RETURN NEW;
END
$func$  LANGUAGE plpgsql;

Löst aus

Der Trigger für Schritt 2 wird pro Spalte ausgelöst!

CREATE TRIGGER upbef_step1
  BEFORE UPDATE ON tbl
  FOR EACH ROW
  EXECUTE PROCEDURE trg_tbl_upbef_step1();

CREATE TRIGGER upbef_step2
  BEFORE UPDATE OF col ON tbl                -- key element!
  FOR EACH ROW
  EXECUTE PROCEDURE trg_tbl_upbef_step2();

CREATE TRIGGER upbef_step3
  BEFORE UPDATE ON tbl
  FOR EACH ROW
  EXECUTE PROCEDURE trg_tbl_upbef_step3();

Triggernamen sind relevant, da sie in alphabetischer Reihenfolge ausgelöst werden (alle sind BEFORE UPDATE)!

Die Prozedur könnte mit so etwas wie "Per-Not-Column-Triggern" oder einer anderen Methode zum Überprüfen der Zielliste einer UPDATE in einem Trigger vereinfacht werden. Aber ich sehe keinen Grund dafür.

Wenn col eine NULL sein kann, verwenden Sie einen anderen "unmöglichen" Zwischenwert und suchen Sie zusätzlich in Triggerfunktion 1 nach NULL:

IF OLD.col IS NOT DISTINCT FROM NEW.col THEN
    NEW.col := '#impossible_value#';
END IF;

Passen Sie den Rest entsprechend an.

20

In plpgsql können Sie in Ihrer Triggerfunktion Folgendes tun:

IF NEW.column IS NULL THEN
  NEW.column = 'default value';
END IF;
2
Frank Heikens

Ich habe fast auf natürliche Weise eine andere Lösung für ein ähnliches Problem erhalten, da meine Tabelle eine Spalte mit der Semantik "Zeitstempel der letzten Aktualisierung" (nennen wir es UPDT) enthielt.

Daher habe ich beschlossen, neue Werte für source und UPDT nur auf einmal (oder gar nicht) in ein Update aufzunehmen. Da UPDT bei jedem Update geändert werden soll, kann mit einer solchen Richtlinie mithilfe der Bedingung new.UPDT = old.UPDT geschlossen werden, dass für das aktuelle Update keine Quelle angegeben wurde, und die Standardquelle ersetzt werden.

Wenn in der Tabelle die Spalte "Zeitstempel der letzten Aktualisierung" bereits vorhanden ist, ist diese Lösung einfacher als das Erstellen von drei Auslösern. Nicht sicher, ob es besser ist, UPDT zu erstellen, wenn es nicht bereits benötigt wird. Wenn Aktualisierungen so häufig sind, dass die Gefahr einer Zeitstempelähnlichkeit besteht, kann anstelle des Zeitstempels ein Sequenzer verwendet werden.

0
mas.morozov