如何模仿“插入忽略”和“重复键更新”(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$;