如何模仿“插入忽略”和“重复键更新”(SQL合并)与PostgreSQL?

使用postgresql模拟“insert ignore”和“重复键更新”的最佳方法是什么?

尝试做一个更新。 如果它不修改任何意味着它不存在的行,那么插入也是如此。 显然,你在交易中这样做。

如果你不想把额外的代码放在客户端,你当然可以把它封装在一个函数中。 在这个想法中,你还需要一个非常罕见的竞争条件的循环。

在文档中有一个例子: http : //www.postgresql.org/docs/9.3/static/plpgsql-control-structures.html ,例子40-2在底部。

这通常是最简单的方法。 你可以用规则来做一些魔术,但是可能会变得更加混乱。 我会build议在任何一天的function包装。

这适用于单行或几行值。 如果你正在处理来自子查询的大量行,你最好把它分成两个查询,一个用于INSERT,另一个用于UPDATE(当然是一个合适的join / subselect) – 不需要编写main过滤两次)

编辑:如果你错过了沃伦的答案 ,PG9.5现在有这个本地; 时间升级!


基于Bill Karwin的回答,阐明基于规则的方法将会是什么样子(从同一个数据库中的另一个模式转换为多列主键):

 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"; 

注意:该规则适用于所有INSERT操作,直到该规则被删除,因此不是特别的。

使用PostgreSQL 9.5,现在是原生function (就像MySQL已经有好几年了):

INSERT …在冲突中没有/更新(“UPSERT”)

9.5支持“UPSERT”操作。 INSERT被扩展为接受一个ON CONFLICT DO UPDATE / IGNORE子句。 本条款规定了可能的重复违规行为。

新语法的进一步例子:

 INSERT INTO user_logins (username, logins) VALUES ('Naomi',1),('James',1) ON CONFLICT (username) DO UPDATE SET logins = user_logins.logins + EXCLUDED.logins; 

要获得插入忽略逻辑,你可以做下面的事情。 我发现简单插入从最好的文字值的select语句,然后你可以用NOT EXISTS子句掩盖重复的键。 为了获得重复的逻辑更新,我怀疑pl / pgsql循环将是必要的。

 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) ) 
 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

你可以为一个给定的表创build一个ON INSERT规则,如果一行存在给定的主键值,或者如果存在一个具有给定主键值的行,那么它将执行UPDATE而不是INSERT

我自己也没有尝试过,所以我不能凭经验说话或举个例子。

对于那些Postgres 9.5或更高版本的用户来说,新的ON CONFLICT DO NOTHING语法应该可以工作:

 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; 

对于我们这些拥有更早版本的人来说,这个正确的连接将会起作用:

 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; 

这个解决scheme避免使用规则:

 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 ):

包含EXCEPTION子句的块比没有块的块更昂贵。 因此,不要在没有需要的情况下使用EXCEPTION。

在批量上,您总是可以在插入之前删除该行。 删除不存在的行不会导致错误,因此安全地跳过。

对于数据导入脚本,以某种方式取代“IF NOT EXISTS”(IF NOT EXISTS)在某种程度上可能会有些尴尬,

 DO $do$ BEGIN PERFORM id FROM whatever_table; IF NOT FOUND THEN -- INSERT stuff END IF; END $do$;