how to emulate “insert ignore” and “on duplicate key update” (sql merge) with postgresql?
某些SQL服务器具有一个功能,如果它违反主/唯一键约束,则跳过
使用PostgreSQL模拟
使用PostgreSQL 9.5,这是现在的本机功能(就像MySQL已经有几年了):
INSERT ... ON CONFLICT DO NOTHING/UPDATE ("UPSERT")
9.5 brings support for"UPSERT" operations.
INSERT is extended to accept an ON CONFLICT DO UPDATE/IGNORE clause. This clause specifies an alternative action to take in the event of a would-be duplicate violation.
...
Further example of new syntax:
1 2 3 4 | INSERT INTO user_logins (username, logins) VALUES ('Naomi',1),('James',1) ON CONFLICT (username) DO UPDATE SET logins = user_logins.logins + EXCLUDED.logins; |
编辑:如果你错过了沃伦的回答,那么PG9.5现在已经有了这个原因;时间升级!
在Bill Karwin的回答的基础上,阐明基于规则的方法将是什么样的(从同一个数据库中的另一个模式转移,并使用多列主键):
1 2 3 4 5 6 | CREATE RULE"my_table_on_duplicate_ignore" AS ON INSERT TO"my_table" WHERE EXISTS(SELECT 1 FROM my_table WHERE (pk_col_1, pk_col_2)=(NEW.pk_col_1, NEW.pk_col_2)) DO INSTEAD NOTHING; INSERT INTO my_table SELECT * FROM another_schema.my_table WHERE some_cond; DROP RULE"my_table_on_duplicate_ignore" ON"my_table"; |
注意:该规则适用于所有
尝试做一个更新。如果它没有修改任何意味着它不存在的行,那么插入也是如此。显然,您在事务中执行此操作。
如果您不想在客户端添加额外的代码,您当然可以将其包装在一个函数中。在这种想法中,你还需要一个非常罕见的竞争条件的循环。
文档中有一个例子:http://www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html,示例40-2就在底部。
这通常是最简单的方法。你可以用规则做一些魔术,但它可能会变得更加混乱。我建议在任何一天使用包装函数方法。
这适用于单行或几行值。如果你正在处理大量的行,例如来自子查询,你最好把它分成两个查询,一个用于INSERT,一个用于UPDATE(当然是一个适当的连接/子选择 - 不需要写你的主要过滤两次)
对于那些拥有Postgres 9.5或更高版本的人来说,新的ON CONFLICT DO NOTHING语法应该有效:
1 2 3 4 | INSERT INTO target_table (field_one, field_two, field_three ) SELECT field_one, field_two, field_three FROM source_table ON CONFLICT (field_one) DO NOTHING; |
对于我们这些拥有早期版本的人来说,这种正确的联接将起作用:
1 2 3 4 5 | INSERT INTO target_table (field_one, field_two, field_three ) SELECT source_table.field_one, source_table.field_two, source_table.field_three FROM source_table LEFT JOIN target_table ON source_table.field_one = target_table.field_one WHERE target_table.field_one IS NULL; |
要获得插入忽略逻辑,您可以执行以下操作。我发现只是从文字值的select语句插入效果最好,然后你可以用NOT EXISTS子句掩盖重复的键。为了获得重复逻辑的更新,我怀疑需要一个pl / pgsql循环。
1 2 3 4 5 6 7 8 9 10 | INSERT INTO manager.vin_manufacturer (SELECT * FROM( VALUES ('935',' Citro?n Brazil','Citro?n'), ('ABC', 'Toyota', 'Toyota'), ('ZOM',' OM','OM') ) AS tmp (vin_manufacturer_id, manufacturer_desc, make_desc) WHERE NOT EXISTS ( --ignore anything that has already been inserted SELECT 1 FROM manager.vin_manufacturer m WHERE m.vin_manufacturer_id = tmp.vin_manufacturer_id) ) |
1 2 3 | INSERT INTO mytable(col1,col2) SELECT 'val1','val2' WHERE NOT EXISTS (SELECT 1 FROM mytable WHERE col1='val1') |
看起来PostgreSQL支持称为规则的模式对象。
http://www.postgresql.org/docs/current/static/rules-update.html
您可以为给定的表创建规则
我自己没试过,所以我不能从经验中说话或提供一个例子。
正如@hanmari在评论中提到的那样。当插入postgres表时,on conflict(..)什么都不做是用于不插入重复数据的最佳代码:
1 2 | query ="INSERT INTO db_table_name(column_name) VALUES(%s) ON CONFLICT (column_name) DO NOTHING;" |
ON CONFLICT代码行将允许insert语句仍然插入数据行。查询和值代码是从Excel插入postgres db表的日期示例。
我在postgres表中添加了约束,用于确保ID字段是唯一的。我没有对相同的数据行执行删除操作,而是添加了一行sql代码,用于重新编号从1开始的ID列。
例:
1 | q = 'ALTER id_column serial RESTART WITH 1' |
如果我的数据有ID字段,我不会将其用作主ID /序列ID,我创建一个ID列并将其设置为serial。
我希望这些信息对每个人都有帮助。
*我没有软件开发/编码的大学学位。我在编码时所知道的一切,都是我自己研究的。
此解决方案避免使用规则:
1 2 3 4 5 6 | BEGIN INSERT INTO tableA (unique_column,c2,c3) VALUES (1,2,3); EXCEPTION WHEN unique_violation THEN UPDATE tableA SET c2 = 2, c3 = 3 WHERE unique_column = 1; END; |
但它有性能缺陷(见PostgreSQL.org):
A block containing an EXCEPTION clause is significantly more expensive
to enter and exit than a block without one. Therefore, don't use
EXCEPTION without need.
批量处理时,您始终可以在插入之前删除该行。删除不存在的行不会导致错误,因此可以安全地跳过它。
对于数据导入脚本,要取代"IF NOT EXISTS",在某种程度上,有一个稍微尴尬的配方仍然有效:
1 2 3 4 5 6 7 8 9 10 11 | DO $do$ BEGIN PERFORM id FROM whatever_table; IF NOT FOUND THEN -- INSERT stuff END IF; END $do$; |