MERGE 是在 PostgreSQL 版本 15 中引入的,它将对您编写查询的方式产生重大影响。这肯定会改变您处理事务日志和其他类似更新的方式。
想象一下,你有一个现有的表格,里面装满了有用的数据,比如你公司销售的商品库存,你会不时地得到数据集,其中包含需要输入的新产品或可能有也可能没有的库存列表。新数量的手头货物。这通常是您在应用程序中必须完成的工作类型。使用应用程序执行此操作的问题是数据必须来回移动,可能多次,从数据库到应用程序,这是很多开销(在这种情况下发音为“时间”) . 所以现在所有这些工作都可以在数据库服务器上用一个命令完成。
SQL Server 和 Oracle 都有这个运算符,它已经成为 SQL 标准的一部分已有一段时间了,PostgreSQL 最近在版本 15 中添加了它。您可以在一个语句中执行 INSERT、UPDATE 和 DELETE。虽然语法看起来有点复杂,但它远远超出了您很快就会看到的简单“upsert”。
术语
我们将有两个表要用 MERGE 处理。具有更新的表称为源表,数据的主要存储库称为目标表。将源表的内容与目标表进行比较,并对目标表进行操作。行被认为在两个表之间匹配或不匹配,并且对这些匹配采取相应的操作。
工作原理示例
让我们从两个表和一行数据开始。
test=# create table a (id int, x int, status char(10));
CREATE TABLE
test=# create table b (id int, x int, status char(10));
CREATE TABLE
test=# insert into a (id,x,status) values (1,1,'From a');
INSERT 0 1
test=# select * from a;select * from b;
id | x | status
----+---+------------
1 | 1 | From a
(1 row)
id | x | status
----+---+--------
(0 rows)
现在我们可以在查询中使用 MERGE。请记住,当您查看下面的查询时,表 a有一行数据而表 b没有任何数据行。在此查询中,我们插入不在目标表中的记录并更新那些在目标表中的记录。
Shell
MERGE into b using a on a.id = b.id
when matched then
update set x = b.x + 1
when not matched then
insert (id,x,status) values (a.id,a.x,a.status);
该查询指示服务器在 id 列上匹配两个表。如果有匹配的行,则表 b中的列x递增 1。但是,如果没有匹配项(因为表 b为空,所以不会有匹配项),那么表 a中行的内容将复制到表 b中。
如果我们明确地查看两个表的内容,我们会发现这两个表现在是相同的。
test=# select * from a; select * from b;
id | x | status
----+---+------------
1 | 1 | From a
(1 row)
id | x | status
----+---+------------
1 | 1 | From a
(1 row)
如果我们在查询中使用 EXPLAIN,我们会得到以下结果:
test-# update set x = b.x + 1
test-# when not matched then insert (id,x,status) values (a.id,a.x,a.status);
QUERY PLAN
----------------------------------------------------------------
Merge on b (cost=0.00..1.02 rows=0 width=0)
-> Nested Loop Left Join (cost=0.00..1.02 rows=1 width=25)
Join Filter: (a.id = b.id)
-> Seq Scan on a (cost=0.00..1.01 rows=1 width=19)
-> Seq Scan on b (cost=0.00..0.00 rows=1 width=10)
(5 rows)
test=#
并注意更新行需要是update set x = bx +1而不是update set bx =bx + 1因为它会触发语法错误。另外,您不能更新表 a – 只能更新目标表。
再次运行 MERGE
现在再次运行 MERGE 查询并查看两个表的内容。这次执行了匹配的逻辑,x 列的值从 1 增加到 2。
test=# select * from a; select * from b;
id | x | status
----+---+------------
1 | 1 | From a
(1 row)
id | x | status
----+---+------------
1 | 2 | From a
(1 row)
test=#
是的,每次我们重新运行 MERGE 查询时,列 x 都会递增。
删除!
如果找到匹配的行,DELETE 将从目标表 b中删除行。让我们更改查询,而不是增加列的值,我们只删除目标表中的匹配行。
test=# merge into b using a on a.id = b.id
test-# when matched then delete
test-# when not matched then insert (id,x,status) values (a.id,a.x,a.status);
MERGE 1
test=# select * from b;
id | x | status
----+---+--------
(0 rows)
什么也不做
我们的选择之一是什么都不做。是的,我们发现该记录已经存在于源表中,因此可以跳过它。目前,表 b 在 DELETE 示例之后没有任何内容。因此,运行以下查询以填充表,然后再次运行。
test=# MERGE into b using a on a.id = b.id
test-# when matched then
test-# DO NOTHING
test-# when not matched then
test-# insert (id,x,status) values (a.id,a.x,a.status);
MERGE 1
test=#
太复杂了!
您可以根据需要使 MERGE 变得尽可能复杂。在下文中,您将看到我们可以对目标表的列的不同值进行操作。我们首先截断目标表,然后对第一个流行表 b 运行 MERGE 查询。然后我们第二次运行它并注意x和状态列的变化情况。当第三次运行 MERGE 查询时,列将再次更新。
test=# truncate b;
TRUNCATE TABLE
test=# select * from b;
id | x | status
----+---+--------
(0 rows)
merge into b
using a
on b.id = a.id
when matched AND b.x > 2 THEN
UPDATE SET x = b.x + a.x, status='updated+'
when matched and b.x = 1 THEN
UPDATE SET status = 'updated', x = 3
when not matched then
insert (id,x,status) values (a.id,a.x,a.status);
现在运行 MERGE 查询一次。
test=# select * from b;
id | x | status
----+---+------------
1 | 1 | From a
(1 row)
现在再次运行它。
test=# select * from b;
id | x | status
----+---+------------
1 | 3 | updated
(1 row)
现在是第三次!
test=# select * from b;
id | x | status
----+---+------------
1 | 4 | updated+
(1 row)
逻辑可以像你能忍受的那样复杂。
结论
MERGE 非常强大,即使是这些非常简单的示例也可以让您瞥见它的用法。许多年前,我正在从事一个项目,该项目具有来自世界各地收银机的交易日志,这些交易日志必须合并,如果 MERGE 可用,它将被大量使用。通读文档和本博客,我相信您会开始思考一些更常见的查询,以及它们如何从 MERGE 运算符中获益。
随着您的表变大,请务必仔细检查您是否在正确的列上使用索引并使用 EXPLAIN 进行仔细检查。
原文标题:Using MERGE To Make Your PostgreSQL More Powerful
原文作者:David Stokes
原文链接:https://www.percona.com/blog/using-merge-to-make-your-postgresql-more-powerful/




