暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

【译】PostgreSQL 15 – 添加对 MERGE SQL 命令的支持

原创 QIU飞 2022-05-27
1223

添加对 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。

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

评论