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 子句来跳过不会改变任何内容的空更新 - 也跳过触发器下方。如果您的大多数新行版本没有改变,这可能会产生巨大的不同。见:

How do I (or can I) SELECT DISTINCT on multiple columns?

要跟踪旧行版本,您可以创建一个具有相同结构的备份表(我们称之为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 和跟踪旧值的主要内容,如果未能解决你的问题,请参考以下文章

使用 MSSQL 中的触发器跟踪和存储新旧值

不能对现有成员使用 upsert

检测模型的变化; php yii 框架

目标跟踪(3)MultiTracker : 基于 OpenCV (C++/Python) 的多目标跟踪

ios-coredata:内存跟踪和完全持久跟踪有啥区别?

跟踪 JS 对象跟踪器的眼睛