8

我有一个表测试的触发函数,它具有以下代码片段:

IF TG_OP='UPDATE' THEN
    IF OLD.locked > 0 AND
 (       OLD.org_id <> NEW.org_id OR
            OLD.document_code <> NEW.document_code OR
            -- other columns ...
 )
THEN
    RAISE EXCEPTION 'Message';
-- more code

所以我静态地检查所有列的新值和它以前的值,以确保完整性。现在,每次我的业务逻辑发生变化并且我必须向该表中添加新列时,我都必须每次修改这个触发器。我认为如果我能以某种方式动态检查该表的所有列,而不显式输入它们的名称,那会更好。

怎么做到呢?

4

5 回答 5

12

从关于触发器中子句的 9.0 beta2 文档WHEN开始,它可能能够在触发器主体中的早期版本中使用:

OLD.* IS DISTINCT FROM NEW.*

或者可能(来自 8.2 发行说明

IF row(new.*) IS DISTINCT FROM row(old.*)

于 2010-06-21T11:33:09.877 回答
11

看一下information_schema,里面有个视图“columns”。执行查询以从触发触发器的表中获取所有当前列名:

SELECT 
    column_name 
FROM 
    information_schema.columns 
WHERE 
    table_schema = TG_TABLE_SCHEMA 
AND 
    table_name = TG_TABLE_NAME;

循环遍历结果,然后就可以了!

更多信息可以在精美的手册中找到。

于 2010-06-21T11:18:56.363 回答
3

在 Postgres 9.0 或更高版本中,在触发器定义(语句)中添加一个WHEN子句:CREATE TRIGGER

CREATE TRIGGER foo
BEFORE UPDATE
FOR EACH ROW
WHEN (OLD IS DISTINCT FROM NEW)  -- parentheses required!
EXECUTE PROCEDURE ...;

仅适用于触发器BEFORE/ AFTER UPDATE,其中定义了OLD和。NEW尝试将此WHEN子句与INSERTorDELETE触发器一起使用时,您会遇到异常。

并相应地从根本上简化触发功能

...
IF OLD.locked > 0 THEN
   RAISE EXCEPTION 'Message';
END IF;
...

无需测试IF TG_OP='UPDATE' ...,因为此触发器仅适用于任何情况UPDATE

或者将该条件也移到 WHEN 子句中:

CREATE TRIGGER foo
BEFORE UPDATE
FOR EACH ROW
WHEN (OLD.locked > 0
  AND OLD IS DISTINCT FROM NEW)
EXECUTE PROCEDURE ...;

在您的触发函数中只保留一个无条件RAISE EXCEPTION,仅在需要开始时调用。

阅读细则:

BEFORE触发器中,WHEN条件在函数正在执行或将要执行之前被评估,因此使用WHEN与在触发器函数开始时测试相同条件没有本质区别。请特别注意,NEW条件看到的行是当前值,可能已被早期触发器修改。此外,BEFORE触发器的WHEN条件不允许检查行的系统列NEW(例如oid),因为尚未设置这些列。

AFTER触发器中,WHEN在行更新发生后立即评估条件,并确定事件是否排队以在语句结束时触发触发器。因此,当AFTER触发器的 WHEN条件未返回 true 时,无需将事件排队,也无需在语句末尾重新获取行。如果只需要为少数几行触发触发器,这可能会导致修改多行的语句显着加速。

有关的:

还要解决问题标题

是否可以动态循环遍历表的列?

是的。例子:

于 2018-04-09T21:33:21.393 回答
2

迭代触发器内的 RECORD 变量

于 2012-03-02T08:24:57.480 回答
1

使用 pl/perl 或 pl/python。它们更适合此类任务。好多了

您也可以安装hstore-new,并使用它的 row->hstore 语义,但是在使用普通数据类型时这绝对不是一个好主意。

于 2010-06-21T21:55:10.100 回答