当你更新插入一行时(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]')
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;
不幸的是,没有更短的方法来编写它。您必须在
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
而不仅仅是整行替换。
在需要指定许多列的情况下,一个更简短的替代方案是简单地删除具有重复键的行并重新插入。 例如,在 python 中,您可以尝试在 try 语句中插入并捕获 psycopg2.errors.UniqueViolation,然后先执行删除,然后执行插入。
try:
cursor.execute(insert_query, fields)
except psycopg2.errors.UniqueViolation:
cursor.execute(delete_query)
cursor.execute(insert_query, fields)
我运行的一些计时测试表明这实际上比更新要快一些。