栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

如何在PostgreSQL中进行UPSERT(MERGE,INSERT…ON DUPLICATE UPDATE)?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如何在PostgreSQL中进行UPSERT(MERGE,INSERT…ON DUPLICATE UPDATE)?

9.5及更高版本:

PostgreSQL 9.5
及更高版本的支持
INSERT ... ON ConFLICT (key) DO UPDATe(和ON ConFLICT (key) DO NOTHING)
,即
upsert

与的比较

ON DUPLICATE KEY UPDATE

快速解释。

有关用法,请参见手册,特别是语法图中的conflict_action子句,以及说明性文字。

与下面给出的9.4及更早版本的解决方案不同,此功能可用于多个冲突的行,并且不需要排他锁定或重试循环。

添加功能的提交在这里,关于功能开发的讨论在这里。

如果您使用的是9.5,并且不需要向后兼容,则可以立即停止阅读。

9.4及更高版本:
PostgreSQL没有任何内置UPSERT(或MERGE)功能,面对并发使用要高效地做到这一点非常困难。

本文详细讨论了该问题。

通常,您必须在两个选项之间进行选择:

重试循环中的各个插入/更新操作;或者
锁定表并进行批量合并
个别行重试循环
如果您希望多个连接同时尝试执行插入操作,则在重试循环中使用单个行高位插入是合理的选择。

PostgreSQL文档包含一个有用的过程,可让您在数据库内部循环执行此操作。与大多数幼稚的解决方案不同,它可以防止丢失更新和插入竞争。但是,它将仅在READ COMMITTED模式下工作,并且仅当您在事务中执行唯一操作时才是安全的。如果触发器或辅助唯一键导致唯一违规,则该功能将无法正常工作。

此策略效率很低。只要可行,您都应该将工作排入队列,并按如下所述进行批量追加。

许多尝试解决此问题的方法都没有考虑回滚,因此导致更新不完整。两笔交易相互竞争;他们的成功一个INSERTS; 另一个得到重复的密钥错误,UPDATE而是执行一个。UPDATE等待INSERT回滚或提交的块。当它回滚时,UPDATE条件重新检查会匹配零行,因此即使UPDATE提交实际上并没有完成您期望的更新。您必须检查结果行计数,并在必要时重试。

一些尝试的解决方案也没有考虑SELECT竞争。如果您尝试简单明了的方法:

-- THIS IS WRONG. DO NOT COPY IT. It's an EXAMPLE.BEGIN;UPDATE testtableSET somedata = 'blah'WHERe id = 2;-- Remember, this is WRONG. Do NOT COPY IT.INSERT INTO testtable (id, somedata)SELECT 2, 'blah'WHERe NOT EXISTS (SELECT 1 FROM testtable WHERe testtable.id = 2);COMMIT;

那么当两个同时运行时,会出现几种故障模式。一个问题是已经讨论过的更新重新检查问题。另一个是两个都UPDATe同时匹配零行并继续的地方。然后,他们都做EXISTS测试,这恰好之前的INSERT。两者都获得零行,因此都获得INSERT。一个失败,重复密钥错误。

这就是为什么您需要重试循环的原因。您可能会认为,使用聪明的SQL可以防止重复的键错误或更新丢失,但是您不能这样做。您需要检查行计数或处理重复的键错误(取决于所选方法),然后重试。

请不要为此使用您自己的解决方案。像消息队列一样,这可能是错误的。

带锁的批量更新
有时您想做一个批量上载,在这里您有一个新的数据集要合并成一个较旧的现有数据集。这大大超过各行upserts更高效,更应是首选,只要实用。

在这种情况下,通常按照以下过程操作:

CREATE一张TEMPORARY桌子

COPY 或将新数据批量插入到临时表中

LOCK目标表IN EXCLUSIVE MODE。这允许其他事务对SELECT表进行更改,但不能对其进行任何更改。

做一个UPDATE … FROM的使用临时表中的值的现有记录;

做一个INSERT不已经在目标表中存在的行;

COMMIT,释放锁。

例如,对于问题中给出的示例,使用多值INSERT来填充临时表:

BEGIN;CREATE TEMPORARY TABLE newvals(id integer, somedata text);INSERT INTO newvals(id, somedata) VALUES (2, 'Joe'), (3, 'Alan');LOCK TABLE testtable IN EXCLUSIVE MODE;UPDATe testtableSET somedata = newvals.somedataFROM newvalsWHERe newvals.id = testtable.id;INSERT INTO testtableSELECt newvals.id, newvals.somedataFROM newvalsLEFT OUTER JOIN testtable ON (testtable.id = newvals.id)WHERe testtable.id IS NULL;COMMIT;


转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/634677.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号