PostgreSQL INSERT ON CONFLICT UPDATE (upsert) 使用所有排除的值

问题描述 投票:0回答:3

当你更新插入一行时(PostgreSQL >= 9.5),并且你希望可能的 INSERT 与可能的 UPDATE 完全相同,你可以这样写:

INSERT INTO tablename (id, username, password, level, email) 
                VALUES (1, 'John', 'qwerty', 5, '[email protected]') 
ON CONFLICT (id) DO UPDATE SET 
  id=EXCLUDED.id, username=EXCLUDED.username,
  password=EXCLUDED.password, level=EXCLUDED.level,email=EXCLUDED.email

还有更短的路吗?只是说:使用所有 EXCLUDE 值。

在 SQLite 中我曾经这样做过:

INSERT OR REPLACE INTO tablename (id, user, password, level, email) 
                        VALUES (1, 'John', 'qwerty', 5, '[email protected]')
postgresql upsert postgresql-9.5
3个回答
290
投票

Postgres 尚未实现与

INSERT OR REPLACE
等效的功能。来自
ON CONFLICT
文档
(强调我的):

它可以是 DO NOTHING,也可以是 DO UPDATE 子句,指定发生冲突时要执行的 UPDATE 操作的确切细节

虽然它没有为您提供替换的简写,但

ON CONFLICT DO UPDATE
应用更广泛,因为它允许您根据预先存在的数据设置新值。例如:

INSERT INTO users (id, level)
VALUES (1, 0)
ON CONFLICT (id) DO UPDATE
SET level = users.level + 1;

16
投票

不幸的是,没有更短的方法来编写它。您必须在

do update
部分指定要更新的每一列。

INSERT INTO tablename (id, username, password, level, email, update_count) 

-- if id doesn't exist, do insert
VALUES (1, 'John', 'qwerty', 5, '[email protected]', 0) 

-- how to check for duplicates (more versatile: could use any unique index here)
ON CONFLICT (id) 
DO UPDATE 
SET 
  -- update duplicate clause
  username=EXCLUDED.username, -- references proposed insertion row
  password=EXCLUDED.password,
  level=EXCLUDED.level,
  email=EXCLUDED.email,
  update_count=tablename.update_count+1 -- reference existing row

on conflict
将为您提供与 sqlite 中的
insert or replace
类似的功能,但它是一个更通用的功能,更专注于
update
而不仅仅是整行替换。


0
投票

在需要指定许多列的情况下,一个更简短的替代方案是简单地删除具有重复键的行并重新插入。 例如,在 python 中,您可以尝试在 try 语句中插入并捕获 psycopg2.errors.UniqueViolation,然后先执行删除,然后执行插入。

try:
    cursor.execute(insert_query, fields)
except psycopg2.errors.UniqueViolation:
    cursor.execute(delete_query)
    cursor.execute(insert_query, fields)

我运行的一些计时测试表明这实际上比更新要快一些。

© www.soinside.com 2019 - 2024. All rights reserved.