如何为没有已知列的 PostgreSQL 视图编写通用更新触发器?

Posted

技术标签:

【中文标题】如何为没有已知列的 PostgreSQL 视图编写通用更新触发器?【英文标题】:How to write a generic update trigger for PostgreSQL view without known columns? 【发布时间】:2020-10-16 00:31:38 【问题描述】:

我正在使用 PostgreSQL 视图来模拟一个已被拆分为两个单独表的旧表,因此我们可以保持与一系列服务的向后兼容性。目标本质上是让这个视图透明地运行,就好像它是拆分前的原始表一样。使用INSTEAD OF INSERT 触发器,我已经能够非常轻松地处理插入两个表的操作。

但是我正在努力弄清楚如何编写 INSTEAD OF UPDATE 触发器,因为我不知道在任何一个请求中哪些列会被更改(我正在使用 sequelize 所以我真的没有任何办法控制请求将是什么,可能是SET 单个列,也可能是所有请求)。我在网上找到的所有示例似乎都在更新已知字段(与查询相关的一些元列或知道更新的形状)。

CREATE FUNCTION update_legacy_table_trigger()
RETURNS trigger AS $$
  BEGIN
    UPDATE table_a SET ??? WHERE id = NEW.id;
    UPDATE table_b SET ??? WHERE table_a_id = NEW.id; 
  END;
$$ LANGUAGE plpgsql;

几个例子:

想象一下:

legacy_table 是视图的名称 table_a 具有以下列:idnamedescriptiontypeshared_columntable_b 具有以下列:idtable_a_idtable_a 的外键)、priceshared_column

示例 A:

UPDATE legacy_table SET name="Test", price="10.00" WHERE id="123";

我希望触发器的行为类似于:

UPDATE table_a SET name="Test" WHERE id="123";
UPDATE table_b SET price="10.00" WHERE table_a_id="123";

示例 B:它也可以只接收一张表的更新

UPDATE legacy_table SET price="10.00" WHERE id="123";

所以我们希望触发器的行为类似于:

UPDATE table_b SET price="10.00" WHERE table_a_id="123";

示例 C:可能在一个请求中有很多列

UPDATE legacy_table SET name="Test", price="10.00", description="A test", type="foo", shared_column="bar" WHERE id="123";

所以触发器的行为应该是这样的:

UPDATE table_a SET name="Test", description="A test", type="foo", shared_column="bar" WHERE id="123";
UPDATE table_b SET price="10.00", shared_column="bar" WHERE table_a_id="123";

可能存在需要对两个表进行更新的情况,但我认为原始 UPDATE 中的列名不会与新表。

在我不明确知道 UPDATE 查询是什么的情况下,我该如何编写这个 INSTEAD OF UPDATE 触发器?

【问题讨论】:

【参考方案1】:

您必须为触发器函数中的所有列编写更新语句。您的触发函数如下所示:

CREATE FUNCTION update_legacy_table_trigger()
RETURNS trigger AS $$
  BEGIN
    UPDATE table_a SET name=new.name, description=new.description,type=new.type, shared_column=new.shared_column WHERE id = NEW.id;
    UPDATE table_b SET price=new.price, shared_columns=new.shared_columns WHERE table_a_id = NEW.id; 
    return null;
  END;
$$ 
LANGUAGE plpgsql;

你必须在你的视图上写下你的触发器,如下所示:

CREATE TRIGGER trg_update_legacy 
INSTEAD OF UPDATE
ON legacy_table
FOR EACH ROW
EXECUTE PROCEDURE update_legacy_table_trigger();

DEMO

这将处理所有更新情况,因为NEW 将包含更新的行,我们正在使用NEW 行的数据更新所有字段。

【讨论】:

因此它只会忽略更新中未包含的字段。多么简单。非常感谢您清晰而全面的回答。

以上是关于如何为没有已知列的 PostgreSQL 视图编写通用更新触发器?的主要内容,如果未能解决你的问题,请参考以下文章

如何为更新列的数量是动态的编写更新语句

如何为 GET 和 POST 编写单独的视图

如何为多个转换编写 Iconverter

如何为 dojox.mobile 视图编写可重用的控制器代码

如何为 postgresql MATERIALIZED VIEW 创建 typeorm 实体

如何为csv文件的column2,column 3编写if条件