Postgresql

分配具有動態列名的列

  • July 21, 2016

我獲得了要在 ( ) 觸發器中設置的列的名稱BEFORE UPDATE,我想將其設置為該OLD值並忽略任何傳入的內容。我嘗試了以下方法:

CREATE OR REPLACE FUNCTION prevent_column_update() RETURNS TRIGGER AS $$
 DECLARE
   col TEXT := TG_ARGV[0];
 BEGIN
   EXECUTE format('SELECT ($1).%I INTO ($2).%I', col, col) USING OLD, NEW;
   RETURN NEW;
 END;
 $$ LANGUAGE plpgsql;

並且可以像這樣使用:

CREATE TRIGGER request_protect_date_price_value
 BEFORE UPDATE OF date_price ON requests
 FOR EACH ROW EXECUTE PROCEDURE prevent_column_update('date_price');

但是當更新失敗時:

ERROR:  syntax error at or near "("
LINE 1: SELECT ($1).date_price INTO ($2).date_price
                                   ^
QUERY:  SELECT ($1).date_price INTO ($2).date_price

錯誤是該INTO子句不是SQL 命令的一部分。它是 plpgsql 命令的一部分EXECUTE

動態欄位名稱目前是不可能的,無論是在 SQL 還是 PL/pgSQL 中。但是有一些方法可以繞過這個限制:

概念證明

您可以(ab)使用內置JSON 函式json_populate_record()來實現類似的技巧,但目前沒有記錄,並且可能在 Postgres 的未來版本中被刪除。

可靠的方法是使用附加hstore模組的文件化**#=操作符。**每個數據庫安裝一次模組

CREATE EXTENSION IF NOT EXISTS hstore;

然後:

CREATE OR REPLACE FUNCTION prevent_column_update()
 RETURNS TRIGGER AS
$func$
DECLARE
  _col text := quote_ident(TG_ARGV[0]);
  _old_val text;
  _new_val text;
BEGIN
  EXECUTE format('SELECT $1.%1$I, $2.%1$I', _col)
  INTO    _old_val, _new_val  -- part of plpgsql command
  USING   OLD, NEW;

  IF _old_val IS DISTINCT FROM _new_val THEN  -- only if it actually changed
     NEW := NEW #= hstore(_col, _old_val);    -- hstore operator #=
  END IF;

  RETURN NEW;
END
$func$ LANGUAGE plpgsql;

請注意,hstore使用文本字元串進行操作。其他數據類型的值被強制轉換text,這適用於我能想到的任何數據類型。但它可能會導致某些類型出現問題(例如浮點數的捨入錯誤)。

而這個觸發器定義使案例完整:

CREATE TRIGGER tbl_upbef_nope
BEFORE UPDATE ON tbl  -- your table here
FOR EACH ROW
EXECUTE PROCEDURE prevent_column_update('date_price');

在此範例中,列名區分大小寫,因為它作為字元串而不是標識符傳遞。

我會做什麼

只需為每個表編寫一個沒有動態 SQL 的新普通觸發器函式。更少的麻煩,更好的性能。關於程式碼重複的要點:

CREATE OR REPLACE FUNCTION prevent_column_update()
 RETURNS TRIGGER AS
$func$
BEGIN
  NEW.date_price:= OLD.date_price;  -- unconditionally
  RETURN NEW;
END
$func$ LANGUAGE plpgsql;

扳機:

CREATE TRIGGER tbl_upbef_nope
BEFORE UPDATE OF date_price ON tbl  -- your column and table here
FOR EACH ROW EXECUTE PROCEDURE prevent_column_update();  -- no param

我將檢查移至觸發器本身,因此除非更新列,否則該函式甚至不會執行。請注意,這可以通過同一張表上的其他觸發器來規避,因為(引用手冊):

僅當列出的列中的至少一個列被提及為命令的目標時,觸發器才會觸發UPDATE

因此,如果您無法排除此類額外觸發器,UPDATE請無條件觸發觸發器並檢查觸發器函式中的更改。

引用自:https://dba.stackexchange.com/questions/144388