检测 postgres 更新触发器中的列更改

Posted

技术标签:

【中文标题】检测 postgres 更新触发器中的列更改【英文标题】:Detecting column changes in a postgres update trigger 【发布时间】:2014-05-21 16:08:53 【问题描述】:

我有一个 postgres 数据库,其中包含几个表,我想观察它们的更新,如果有任何更新,我想触发“嘿,有些东西改变了”更新。这在基本情况下有效,但现在是时候改进了。

CREATE FUNCTION notify_update() RETURNS trigger AS $notifyfunction$
BEGIN
  PERFORM pg_notify('update_watchers',
    $$"event":"update", "type": "$$ || TG_TABLE_NAME || $$", "payload": "id": $$ || new.id || $$$$);
  RETURN new;
END;
$notifyfunction$ LANGUAGE plpgsql;

工作得很好。我像这样将它附加到桌子上:

CREATE TRIGGER document_update_body
AFTER UPDATE ON documents
FOR EACH ROW EXECUTE PROCEDURE notify_update();

(作为一个附带问题:如果有比触发器函数中的 mess'o'$$ 更好/更简单的方法来 json.stringify 我的触发器结果,请告诉我。平衡引号并不好玩) .

我想要做的是附加到 pg_notify 调用已更改的列的列表。 似乎除了迭代表中的列并检查 NEW.col 是否与 OLD.col 不同之外,没有任何简单的方法可以做到这一点。这样做的不好的方法是在我的通知过程中对列名进行硬编码(脆弱,如果我更改我的架构等要更新的另一件事)。

我在编写 plpgsql 方面也没有深度,真的,所以我不知道去哪里寻求帮助。理想情况下,(如果没有我在文档中没有看到的 updated_columns 块变量),有一种方法可以在通知块中获取表的架构,而不会造成太严重的性能开销(因为这些表将得到更新公平一点)。

【问题讨论】:

【参考方案1】:

阅读hstore 分机。特别是,您可以从一行创建一个 hstore,这意味着您可以执行以下操作:

changes := hstore(NEW) - hstore(OLD);
...pg_notify(... changes::text ...)

这比您想要的信息略多(包括新值)。如果您只想要密钥,可以使用akeys(changed)

【讨论】:

这太完美了——我做了 pg_notify ... hstore_to_json(hstore(NEW) - hstore(OLD)) ...,除了数组类型列的变化(它们被转换成字符串)外,它的效果很好在结果 json 中看起来像 Postgres 数组 "1, 2, 3" 而不是实际的 "val": [1, 2, 3] 家伙,但我可以在客户端处理它,因为它是一个解析问题. 在 9.3 中有 json 类型,在 9.4 中加入了许多改进mrnts(截至目前为测试版)。值得一试。【参考方案2】:

http://www.postgresql.org/docs/9.3/static/plpython-trigger.html

TD["table_name"]

我执行完全相同类型的通知,我像这样遍历所有列:

    for k in TD["new"]:
        if TD["old"][k] != TD["new"][k]:
            changed.append(k)

changed.append(k) 构建我的通知字符串。我在其他地方进行监听,然后将结果从 pub/sub 广播到 Web 套接字客户端。

-g

【讨论】:

【参考方案3】:

另一种方法是利用 PostgreSQL 最新版本中的 JSON/JSONB 函数。它的优点是可以处理任何可以转换为 JSON 对象(行或任何其他结构化数据)的东西,而且您甚至不需要知道记录类型。

请参阅我原来的*** post 并提供适当的示例。

【讨论】:

以上是关于检测 postgres 更新触发器中的列更改的主要内容,如果未能解决你的问题,请参考以下文章

mysql 日志记录触发器,查找更改的列

创建一个触发器,当另一个表中的列更新时更新一个表上的列

Postgres视图中的默认值,触发代替更新

DB2:忽略更新触发器中的列

如何在更新单独表中的列的视图上创建触发器?

插入后使用 SQL Server 触发器更新另一个表中的列