更新列或列或ColumnS时触发触发器

时间:2014-08-21 20:48:08

标签: postgresql triggers constraints notify

我有代码只在更新单个特定列时触发触发器。触发器用于触发一个函数,该函数将引发一个postgres" notify"我正在监听的事件,需要测试和验证新输入的详细信息。 account_details表上有许多值可以更改,不需要帐户验证,因此仅在AFTER UPDATE(没有时间)的触发器是不好的。

    CREATE TRIGGER trigger_update_account_details
    AFTER UPDATE ON account_details
    FOR EACH ROW
    WHEN (OLD.email IS DISTINCT FROM NEW.email) 
    EXECUTE PROCEDURE notify_insert_account_details();

但是如果许多列中的一列发生变化,我想触发触发器,例如

WHEN (OLD.email IS DISTINCT FROM NEW.email OR 
OLD.username IS DISTINCT FROM NEW.username OR 
OLD.password IS DISTINCT FROM NEW.password) 

但OR不是触发器的有效关键字。试图搜索要使用的关键字而不是OR似乎没有提出任何由于单词OR的性质而引起的: - (

4 个答案:

答案 0 :(得分:22)

这是一种误解。 WHEN clause of the trigger definition expects a boolean expression您可以在其中使用OR运算符。这应该可行(假设所有列实际存在于表account_details中)。我自己也在使用类似的触发器:

CREATE TRIGGER trigger_update_account_details
AFTER UPDATE ON account_details
FOR EACH ROW
WHEN (OLD.email    IS DISTINCT FROM NEW.email
   OR OLD.username IS DISTINCT FROM NEW.username
   OR OLD.password IS DISTINCT FROM NEW.password) 
EXECUTE PROCEDURE notify_insert_account_details();

评估表达式的成本很低,但这可能比其他选择更可靠

CREATE TRIGGER ... AFTER UPDATE OF email, username, password ...

因为,per documentation

  

特定于列的触发器(使用UPDATE OF column_name 定义的触发器   语法)将在其任何列被列为目标中时触发   UPDATE命令的SET列表。列的值可能是   即使触发器未被触发也会改变,因为对其进行了更改   不考虑BEFORE UPDATE触发器的行内容。   相反,诸如UPDATE ... SET x = x ...之类的命令将触发   在列x上触发,即使列的值没有改变。

检查许多列(执行相同操作)时,

ROW类型语法更短:

CREATE TRIGGER trigger_update_account_details
AFTER UPDATE ON account_details
FOR EACH ROW
WHEN ((OLD.email, OLD.username, OLD.password, ...)
       IS DISTINCT FROM
      (NEW.email, NEW.username, NEW.password, ...))
EXECUTE PROCEDURE notify_insert_account_details();

或者,检查行中每个可见用户列:

...
WHEN (OLD IS DISTINCT FROM NEW)
...

答案 1 :(得分:6)

我认为你不需要WHEN条款。您可以在UPDATE子句中指定有问题的列:

CREATE TRIGGER trigger_update_account_details
    AFTER UPDATE OF email, username, password ON account_details
    FOR EACH ROW
    EXECUTE PROCEDURE notify_insert_account_details();

答案 2 :(得分:1)

上述解决方案不适用于我。因此,在再次阅读文档之后。我发现没什么要注意的。 UPDATE ON之前-UPDATE ON之后,触发器的执行方式不同。由于我的程序正在返回具有更新值的NEW记录。它不能在AFTER触发器和BEFORE触发器中工作,WHEN子句中的OR语句需要用大括号括起来。

CREATE TRIGGER check_update
BEFORE UPDATE ON some_table
FOR EACH ROW
WHEN ((OLD.colum_name_1 IS DISTINCT FROM NEW.colum_name_1) OR (OLD.colum_name_2 IS DISTINCT FROM NEW.colum_name_2))
EXECUTE PROCEDURE update_updated_at_column();

程序

CREATE OR REPLACE FUNCTION update_updated_at_column()
  RETURNS TRIGGER AS $$
  BEGIN
      NEW.updated_at = now();
      RETURN NEW;
  END;
  $$ language 'plpgsql';

答案 3 :(得分:0)

我有点匆忙,但这是我采用的解决方案。我想在“答案”列更改时更新名为“receivedAt”的列(是的,我的列是驼峰式的,我的表是大写的...不要问...)。如果答案为空,我也希望它为空(边缘情况不应该真正发生)。但我根本不希望每次更新任何行时都触发此触发器,因为这可能代价高昂。

我决定结合上面使用的策略,相信 Postgres 会以高性能的方式做它应该做的事情。我认为他们中的一些人重新发明轮子并且效率低下,因为他们会在任何更新时触发。

我使用 knex 迁移来管理我的数据库,所以我会继续将整个内容粘贴到此处。

import { Knex } from 'knex';


export async function up(knex: Knex): Promise<void> {
  await knex.raw(`
  CREATE OR REPLACE FUNCTION question_update_received_at_when_answer_changes()
  RETURNS TRIGGER
  LANGUAGE PLPGSQL
  AS
  $$
  BEGIN
    NEW."receivedAt" = NOW();
    IF NEW."answer" IS NULL THEN
      NEW."receivedAt" = NULL;
    END IF;
    RETURN NEW;
  END;
  $$;

  DROP TRIGGER IF EXISTS trigger_question_answer_received_at ON "Question";
  CREATE TRIGGER trigger_question_answer_received_at
    BEFORE UPDATE OF "answer" ON "Question"
    FOR EACH ROW
    WHEN (OLD."answer" IS DISTINCT FROM NEW."answer")
    EXECUTE PROCEDURE question_update_received_at_when_answer_changes();
  `)
}


export async function down(knex: Knex): Promise<void> {
  await knex.raw(`
  DROP TRIGGER trigger_question_answer_received_at on "Question";
  DROP FUNCTION question_update_received_at_when_answer_changes;
  `)
}