更新和跟踪旧值
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();
相关:
- FOR EACH STATEMENT trigger example
- Trigger function does not exist, but I am pretty sure it does
我有一个包含 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();
相关:
- FOR EACH STATEMENT trigger example
- Trigger function does not exist, but I am pretty sure it does