web-dev-qa-db-ja.com

トリガー関数内で、更新されているフィールドを取得する方法

これは可能ですか?送信される新しい値が既にデータベースに格納されているものであるかどうかに関係なく、UPDATE要求で指定された列を見つけることに興味があります。

これを行う理由は、複数のソースから更新を受信できるテーブルがあるためです。以前は、更新のソースを記録していませんでした。これで、テーブルには最新の更新を実行したソースが格納されます。一部のソースを変更してIDを送信できますが、それがすべてのオプションになるわけではありません。したがって、UPDATEリクエストに識別子がない場合に認識できるようにして、デフォルト値で置き換えることができます。

28
EvilAmarant7x

「ソース」が「識別子を送信」し​​ない場合、列は変更されません。次に、現在のUPDATEが最後のソースと同じソースによって行われたのか、列をまったく変更しなかったソースによって行われたのかを検出できません。つまり、これは適切に機能しません。

「ソース」が任意の セッション情報関数 で識別可能であれば、それで作業できます。お気に入り:

_NEW.column = session_user;
_

すべての更新に対して無条件。

一般的なソリューション

元の問題を解決する方法を見つけました。列はanyupdateでデフォルト値に設定されますが、列はnotupdateSETUPDATEリストにはありません)。

キー要素は per-column trigger PostgreSQL 9.0で導入されました-_UPDATE OF_を使用した列固有のトリガー_column_name_句。

トリガーは、リストされた列の少なくとも1つがUPDATEコマンドのターゲットとして言及されている場合にのみ起動します。

これが、列が古い値と同じ新しい値で更新されたか、まったく更新されなかったかを区別するために見つけた唯一の簡単な方法です。

1つcouldcurrent_query() によって返されたテキストを解析します。しかし、それはトリッキーで信頼性が低いようです。

トリガー機能

colが_NOT NULL_を定義すると想定します。

ステップ1:変更されていない場合は、colNULLに設定します。

_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;
_

ステップ2:古い値に戻します。トリガーは値が実際に更新された場合にのみ発生します(以下を参照):

_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;
_

ステップ3:これで、不足している更新を識別し、代わりにデフォルト値を設定できます。

_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;
_

トリガー

Step 2のトリガーは列ごとに発生します!

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

トリガー名は、アルファベット順に起動されるため、関連があります(すべて_BEFORE UPDATE_です)!

手順は、「列ごとではないトリガー」のようなもの、またはトリガー内のUPDATEのターゲットリストをチェックする他の方法で簡略化できます。しかし、私はこれに対応できません。

colNULLの場合は、他の「不可能な」中間値を使用し、トリガー関数1でNULLを追加でチェックします。

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

残りを適宜調整してください。

23

Plpgsqlでは、トリガー関数で次のようなことができます:

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

私のテーブルには「最終更新タイムスタンプ」のセマンティクスを持つ列が含まれているため、ほぼ自然に同様の問題の別の解決策が得られました(これをUPDTと呼びます)。

そのため、更新にソースとUPDTの新しい値を一度に1つだけ含めるか、またはそれらに含めないことにしました。 UPDTは更新ごとに変更することを目的としているため、このようなポリシーを使用すると、条件new.UPDT = old.UPDTを使用して、現在の更新でソースが指定されていないことを推定し、デフォルトの更新で置き換えることができます。

テーブルに「最終更新タイムスタンプ」列がすでにある場合、このソリューションは3つのトリガーを作成するよりも簡単です。 UPDTを作成する方が良いかどうか、まだ必要ない場合はわかりません。更新が頻繁でタイ​​ムスタンプの類似性のリスクがある場合は、タイムスタンプの代わりにシーケンサーを使用できます。

0
mas.morozov