我有代码只在更新单个特定列时触发触发器。触发器用于触发一个函数,该函数将引发一个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的性质而引起的: - (
答案 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 ...
检查许多列(执行相同操作)时,特定于列的触发器(使用
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;
`)
}