Don*_*on2 8 postgresql sequence upsert postgresql-13
我在 PostgreSQL 13 中有下表:
field type
----- ----
Seq bigserial
code varchar
Run Code Online (Sandbox Code Playgroud)
Seq
是主键(自增)
Code
是唯一键索引
Insert Into newtable (Code) Values ('001') On Conflict(Code) Do Nothing --> Seq value is 1
Insert Into newtable (Code) Values ('001') On Conflict(Code) Do Nothing
Insert Into newtable (Code) Values ('001') On Conflict(Code) Do Nothing
Insert Into newtable (Code) Values ('002') On Conflict(Code) Do Nothing --> Seq value is 4
Run Code Online (Sandbox Code Playgroud)
为什么是序列4?有什么方法可以仅在成功插入时增加价值?
MERGE
代替使用?MERGE
(在 Postgres 15 中添加)表面上看起来很相似,但在底层却有很大不同。手册:
当
MERGE
与修改目标表的其他命令同时运行时,应用通常的事务隔离规则;有关每个隔离级别的行为的说明,请参见 第 13.2 节。您可能还希望考虑使用作为替代语句,该语句提供 在发生并发时INSERT ... ON CONFLICT
运行的能力。这两种语句类型之间存在多种差异和限制,并且它们不可互换。UPDATE
INSERT
特别是,MERGE
无法可靠地抑制并发写入负载下的独特违规,例如INSERT ... ON CONFLICT DO NOTHING
can (相关命令)。手册:
如果
MERGE
尝试INSERT
存在唯一索引并且同时插入重复行,则会引发唯一性冲突错误;MERGE
不会尝试通过重新开始MATCHED
条件评估来避免此类错误。
不同方法的一个较小(通常不重要)的副作用是MERGE
通常不会烧毁序列号。(它仍然可以,就像引发异常时一样......)
如果您没有并发写入负载,或者所讨论的问题对您来说不重要,那么MERGE
可以作为替代方案。INSERT ... ON CONFLICT DO NOTHING
(那么你可能一开始就不需要。) Rudi添加了一个带有代码示例的答案。
INSERT ... ON CONFLICT DO NOTHING
为什么Seq值不断增加?
原因是在检查重复项(尝试输入索引元组)之前DEFAULT
应用值(以及触发器和任何可能更改行值的其他内容) 。数字旨在防御并发负载下的竞争条件。一旦数字增加,底层证券就不会“收回”数字。还有其他可能会烧毁序列号的情况。因此,序列号存在差距是可以预料的。只要你不以巨大的速度燃烧数字,这应该不是问题。serial
SEQUENCE
有没有办法只有插入成功后才增加Seq值?
并非没有(或多或少)严重影响性能,例如使用SERIALIZABLE
事务隔离或手动锁定策略。这就是为什么ON CONFLICT
这就是子句(“UPSERT”)首先存在的
如果您实际上没有对同一个表进行并发写入(您确定吗?),此替代查询将避免烧毁序列号:
INSERT INTO newtable (code)
SELECT '001'
WHERE NOT EXISTS (SELECT FROM newtable WHERE code = '001';
Run Code Online (Sandbox Code Playgroud)
在非冲突情况下,它的成本稍高,因为它首先检查索引中是否存在,然后实际在表和索引中输入新行。但对于冲突的情况稍微快一些。检查和写入之间有一个很小的窗口,其中竞争条件可能会在并发写入负载下导致问题。这就是我们使用 UPSERT 的时候。
有关的:
PostgreSQL 15 附带了 MERGE 命令。使用此命令进行的插入不会刻录序列号。
CREATE TABLE newtable (
seq INT GENERATED ALWAYS AS IDENTITY,
code varchar UNIQUE
);
Run Code Online (Sandbox Code Playgroud)
运行以下语句几次,然后将值更改001
为002
并重复。Value002
获取分配的下一个可用序列号。
MERGE INTO newtable n
USING (SELECT '001' AS code) AS u
ON (n.code = u.code)
WHEN MATCHED THEN
UPDATE SET
code = u.code
WHEN NOT MATCHED THEN
INSERT (code)
VALUES ('001');
SELECT * FROM newtable ORDER BY 1;
seq code
1 001
2 002
Run Code Online (Sandbox Code Playgroud)
归档时间: |
|
查看次数: |
7236 次 |
最近记录: |