更新和跟踪旧值

Upsert and track old values

我有一个包含 60 万行 属性 列表的供稿,我想每天将其更新到 table。但我也想将旧列(或整行)插入不同的 table 以跟踪数据的变化。例如,我需要知道一些 属性 价格在前一年是如何演变的。

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
      ...;

另一个问题是在另一个 table 中跟踪旧值(正在更新的列)。我读到这可以通过 TRIGGER 来完成。

所以我在想这是否是最好的方法,或者是否还有其他我遗漏的东西。

不要列出 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?

要跟踪旧行版本,您可以创建具有相同结构的备份 table(我们称之为 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();

相关: