【问题标题】:Fire trigger on update of columnA or ColumnB or ColumnC更新列或列或列时触发触发
【发布时间】:2014-08-21 20:48:07
【问题描述】:

我有代码仅在更新单个特定列时触发触发器。触发器用于触发一个函数,该函数将引发一个 postgres“通知”事件,我正在监听该事件,并且需要测试和验证新输入的详细信息。 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 似乎没有提出任何问题:-(

【问题讨论】:

  • 当我阅读 9.3 的文档时,这应该可以工作。你能发布版本和错误信息吗?

标签: postgresql triggers constraints notify


【解决方案1】:

我认为您不需要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();

【讨论】:

  • 我有一种感觉,当通过 UPDATE sql 语句设置值时,这会触发,无论它们是否被认为是“更新”,即不同。
【解决方案2】:

这是一个误解。 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 OFcolumn_name 定义的触发器 syntax) 将在其任何列被列为目标时触发 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)
...

【讨论】:

  • 我想知道使用when 条件和在update on ... 子句中显式列出列之间是否存在性能差异
  • @a_horse_with_no_name:WHEN 条件稍微贵一些(必须对表达式求值),但结果在许多方面也略有不同。我在答案中添加了一些内容。
  • 如果有人担心WHEN 子句的性能,但希望只有在发生实际变化时才执行触发器:这两种解决方案可以结合使用。
  • @pozs:好主意。但是正确性是第一个问题:上述触发器可能会在替代方案不会触发的情况下触发(反之亦然) - 正如引用中所解释的那样。两者都适用时,必须同时满足这两个条件。这取决于具体要求。
  • @malthe:不适用于这个特定问题,但我添加了它以使其完整。不过,我建议使用更短的等效语法:WHEN (OLD IS DISTINCT FROM NEW) - 并且括号是必需的。在此期间,我还在那里添加了另一个答案...
【解决方案3】:

上述解决方案不适用于我。因此,在再次阅读文档后。我发现了一些需要注意的地方。 BEFORE UPDATE ON - AFTER UPDATE ON 触发器的执行方式不同。由于我的程序正在返回具有更新值的新记录。它在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';

【讨论】:

  • 这基本上是我采用的解决方案(在下面发布了更多信息)。但您似乎应该使用BEFORE UPDATE OF column_name_1, column_name_2 ON some_table,这样该触发器就不必在每次更新时评估WHEN 子句
【解决方案4】:

我有点着急,但这是我采用的解决方案。我想在“答案”列更改时更新名为“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;
  `)
}

【讨论】:

  • 对随机的 javascript 内容投反对票。
  • @cpursley 啊哈!! JAVASCRIPT!用火杀死它!感谢您从一个带有 javascript 恶臭的有效解决方案中拯救世界。哈哈。我在帖子中提到我很着急。对于任何不害怕 JS 以至于无法忍受看它的开发人员来说,查看嵌入式 SQL 并不是那么难。而且它绝不是对帖子的价值造成如此大的损害,如果这个帖子不存在会更好。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-08
  • 1970-01-01
相关资源
最近更新 更多