添加对 MERGE SQL 命令的支持。MERGE 执行修改目标表中的行的操作,使用源表或查询。 MERGE 提供了一条 SQL 语句,可以有条件地 INSERT/UPDATE/DELETE 行 - 否则会执行的任务需要多个 PL 语句。例如:
MERGE INTO target AS t USING source AS s ON t.tid = s.sid WHEN MATCHED AND t.balance > s.delta THEN UPDATE SET balance = t.balance - s.delta WHEN MATCHED THEN DELETE WHEN NOT MATCHED AND s.delta > 0 THEN INSERT VALUES (s.sid, s.delta) WHEN NOT MATCHED THEN DO NOTHING;
复制
MERGE 适用于常规表、分区表和继承层次结构,包括列和行安全实施,以及支持行和语句触发器以及其中的转换表。
MERGE 针对 OLTP 进行了优化,并且可以参数化,但也用于大规模 ETL/ELT。 MERGE 不打算优先应用到 INSERT、UPDATE 或 DELETE 的单个 SQL 命令,因为那里是一些开销。可以从 PL/pgSQL 使用 MERGE。
MERGE 不支持以可更新视图或外部表为目标,并且也不允许使用 RETURNING 子句。这些限制很可能花大力才可以修复。也不支持重写规则,但不清楚我们是否愿意支持重写规则。
2015 年,PG 10 我们获得了 INSERT … ON CONFLICT DO …。
然后,在 2018 年,PG 11我们几乎获得了 MERGE功能,但是中途出现了问题,补丁被恢复了。
现在,四年过去了,似乎是时候到了。
现在,让我们看看在一些简单的情况下这些语法如何。
$ create table test ( id int8 generated always as identity, username text not null unique, touch_count int4 not null default 0, primary key (id) ); CREATE TABLE
复制
现在,让我们编写将插入或更新的查询,具体取决于给定用户是否存在:
$ merge into test t using (values ('depesz')) as i(un) on t.username = i.un when matched then update set touch_count = touch_count + 1 when not matched then insert (username, touch_count) values (i.un, 1); MERGE 1
复制
现在我插入了 使用 ‘depesz’,之前表格很清晰。所以表格的内容是……:
$ select * from test; id | username | touch_count ----+----------+------------- 1 | depesz | 1 (1 row)
复制
看起来不错。如果我再次尝试此合并会发生什么?
$ merge into test t using (values ('depesz')) as i(un) on t.username = i.un when matched then update set touch_count = touch_count + 1 when not matched then insert (username, touch_count) values (i.un, 1); MERGE 1 $ select * from test; id | username | touch_count ----+----------+------------- 1 | depesz | 2 (1 row)
复制
很酷的部分是当合并中的 WHEN 子句可以进行更多比较。你就会拥有更多。
例如,假设某个用户被“插入”超过 3 次,它将被删除:
$ merge into test t using (values ('depesz')) as i(un) on t.username = i.un when matched and touch_count < 3 then update set touch_count = touch_count + 1 when matched then delete when not matched then insert (username, touch_count) values (i.un, 1); MERGE 1 $ select * from test; id | username | touch_count ----+----------+------------- 1 | depesz | 3 (1 row) $ merge into test t using (values ('depesz')) as i(un) on t.username = i.un when matched and touch_count < 3 then update set touch_count = touch_count + 1 when matched then delete when not matched then insert (username, touch_count) values (i.un, 1); MERGE 1 $ select * from test; id | username | touch_count ----+----------+------------- (0 rows)
复制
第一次合并效果很好,将 touch_count 增加到 3。第二次看到 touch_count 已经为 3,因此它删除了该行!
有一个重要的缺失特征(在我看来)——缺乏RETURNING。