Upsert 和跟踪旧值
Posted
技术标签:
【中文标题】Upsert 和跟踪旧值【英文标题】:Upsert and track old values 【发布时间】:2021-12-09 23:56:09 【问题描述】:我有一个包含 60 万行房产列表的提要,我希望每天将其更新到表格中。但我还想将旧列(或整行)插入到不同的表中,以跟踪数据的变化。例如,我需要知道某些房地产价格在上一年是如何演变的。
upsert 的问题是需要指定所有字段,因为我想跟踪每一列的更改,因此“ON CONFLICT”子句会变得过于复杂:
INSERT INTO prop_listings (id, prop_type, price, rooms)
VALUES
(33, 'house', 60000, 4)
, (22, 'apartment', 30000, 2)
ON CONFLICT (id, prop_type) -- complicated clause
DO UPDATE SET
prop_type = EXCLUDED.price_usd
, price = EXCLUDED.volume_24h
...;
另一个问题是在另一个表中跟踪旧值(正在更新的列)。我读过这可以通过触发器来完成。
所以我在想这是否是最好的方法,或者我还缺少什么。
【问题讨论】:
【参考方案1】:不要列出ON CONFLICT
子句中的所有列,只列出id
(或构成主键的任何内容)。所以更简单:
INSERT INTO prop_listings AS p (id, prop_type, price, rooms)
VALUES
(33, 'house', 60000, 4)
, (22, 'apartment', 30000, 2)
ON CONFLICT (id) -- simple clause
DO UPDATE
SET prop_type = EXCLUDED.price_usd
, price = EXCLUDED.volume_24h
, rooms = EXCLUDED.rooms
WHERE (p.prop_type, p.price, p.rooms) IS DISTINCT FROM
(EXCLUDED.prop_type, EXCLUDED.price, EXCLUDED.rooms);
我添加了一个WHERE
子句来跳过不会改变任何内容的空更新 - 也跳过触发器下方。如果您的大多数新行版本没有改变,这可能会产生巨大的不同。见:
要跟踪旧行版本,您可以创建一个具有相同结构的备份表(我们称之为prop_backup
)并添加如下触发器:
CREATE OR REPLACE FUNCTION trg_prop_backup()
RETURNS trigger
LANGUAGE plpgsql AS
$func$
BEGIN
INSERT INTO prop_backup -- table has same columns!
VALUES (OLD.*);
RETURN NEW;
END
$func$;
CREATE TRIGGER prop_listings_upd_bef
BEFORE UPDATE ON prop_listings
FOR EACH ROW EXECUTE FUNCTION trg_prop_backup();
相关:
FOR EACH STATEMENT trigger example Trigger function does not exist, but I am pretty sure it does【讨论】:
触发器只插入冲突的值? 我忘了考虑不再出现的行。是否有一个简单的改变来解决这个问题或者是另一个不同的问题? 触发器在更新前复制每一行。应该给个完整的图。如果你想删除“不再出现的行,要么触发ON UPDATE OR DELETE
,要么在删除后手动将它们插入到备份表中。
备份表可能只包含冲突值吗?否则,我每天将在备份表上插入 600k 行。
@Rod0n:这就是解决方案目前所做的。触发器触发ON UPDATE
,即仅当INSERT
发生冲突并且UPDATE
实际更改行时。要像上面提到的那样扩展DELETE
案例的触发器,您需要有条件地返回OLD
。见:***.com/a/23744320/939860以上是关于Upsert 和跟踪旧值的主要内容,如果未能解决你的问题,请参考以下文章