postgresql – 忽略重复插入的最佳方法?
参见英文答案 >
Idiomatic way to implement UPSERT in PostgreSQL3个
背景 此问题涉及使用PostgreSQL 9.2或更高版本忽略重复插入.我问的原因是因为这段代码: -- Ignores duplicates. INSERT INTO db_table (tbl_column_1,tbl_column_2) VALUES ( SELECT unnseted_column,param_association FROM unnest( param_array_ids ) AS unnested_column ); 检查现有值时,代码不受阻碍. (在这种特殊情况下,用户不关心插入重复项的错误 – 插入应该“正常工作”.)在这种情况下添加代码以明确测试重复项会带来复杂性. 问题 在PostgreSQL中,我发现了一些忽略重复插入的方法. 忽略重复#1 创建一个捕获唯一约束违规的事务,不执行任何操作: BEGIN INSERT INTO db_table (tbl_column) VALUES (v_tbl_column); EXCEPTION WHEN unique_violation THEN -- Ignore duplicate inserts. END; 忽略重复#2 创建规则以忽略给定表上的重复项: CREATE OR REPLACE RULE db_table_ignore_duplicate_inserts AS ON INSERT TO db_table WHERE (EXISTS ( SELECT 1 FROM db_table WHERE db_table.tbl_column = NEW.tbl_column)) DO INSTEAD NOTHING; 问题 我的问题主要是学术问题: >哪种方法最有效? 谢谢!
作为另一个问题的答案(其中一个被认为是重复的)提及,(从版本9.5开始)存在本机
UPSERT 功能.对于旧版本,请继续阅读:)
我已经设置了检查选项的测试.我将包含下面的代码,它可以在linux / Unix机器上的psql中运行(仅仅因为为了结果清晰起见,我将设置命令的输出传送到/ dev / null – 在Windows机器上可以选择一个日志文件). 我试图通过在plpgsql存储过程中使用一个循环来运行每种类型多个(即100个)INSERT来使不同的结果具有可比性.此外,在每次运行之前,通过截断并重新插入原始数据来重置表. 检查一些测试运行,看起来像使用规则并显式添加WHERE NOT EXISTS INSERT语句花费相似的时间,而导致异常需要花费更多的时间才能完成. 后者不是surprising:
就个人而言,由于可读性和可维护性,我更喜欢将INSERE NOT EXISTS位添加到INSERT本身.就像触发器(也可以在这里测试)一样,调试(或简单地跟踪INSERT行为)在存在规则时更复杂. 我使用的代码(随意指出误解或其他问题): o /dev/null timing off -- set up data DROP TABLE IF EXISTS insert_test; CREATE TABLE insert_test_base_data ( id integer PRIMARY KEY,col1 double precision,col2 text ); CREATE TABLE insert_test ( id integer PRIMARY KEY,col2 text ); INSERT INTO insert_test_base_data SELECT i,(SELECT random() AS r WHERE s.i = s.i) FROM generate_series(2,200,2) s(i) ; UPDATE insert_test_base_data SET col2 = md5(col1::text) ; INSERT INTO insert_test SELECT * FROM insert_test_base_data ; -- function with exception block to be called later CREATE OR REPLACE FUNCTION f_insert_test_insert( id integer,col2 text ) RETURNS void AS $body$ BEGIN INSERT INTO insert_test VALUES ($1,$2,$3) ; EXCEPTION WHEN unique_violation THEN NULL; END; $body$ LANGUAGE plpgsql; -- function running plain SQL ... WHERE NOT EXISTS ... CREATE OR REPLACE FUNCTION insert_test_where_not_exists() RETURNS void AS $body$ BEGIN FOR i IN 1 .. 100 LOOP INSERT INTO insert_test SELECT i,rnd,md5(rnd::text) FROM (SELECT random() AS rnd) r WHERE NOT EXISTS ( SELECT 1 FROM insert_test WHERE id = i ) ; END LOOP; END; $body$ LANGUAGE plpgsql; -- call a function with exception block CREATE OR REPLACE FUNCTION insert_test_function_with_exception_block() RETURNS void AS $body$ BEGIN FOR i IN 1 .. 100 LOOP PERFORM f_insert_test_insert(i,md5(rnd::text)) FROM (SELECT random() AS rnd) r ; END LOOP; END; $body$ LANGUAGE plpgsql; -- leave checking existence to a rule CREATE OR REPLACE FUNCTION insert_test_rule() RETURNS void AS $body$ BEGIN FOR i IN 1 .. 100 LOOP INSERT INTO insert_test SELECT i,md5(rnd::text) FROM (SELECT random() AS rnd) r ; END LOOP; END; $body$ LANGUAGE plpgsql; o timing on echo echo 'check before INSERT' SELECT insert_test_where_not_exists(); echo o /dev/null timing off TRUNCATE insert_test; INSERT INTO insert_test SELECT * FROM insert_test_base_data ; timing on o echo 'catch unique-violation' SELECT insert_test_function_with_exception_block(); echo echo 'implementing a RULE' o /dev/null timing off TRUNCATE insert_test; INSERT INTO insert_test SELECT * FROM insert_test_base_data ; CREATE OR REPLACE RULE db_table_ignore_duplicate_inserts AS ON INSERT TO insert_test WHERE EXISTS ( SELECT 1 FROM insert_test WHERE id = NEW.id ) DO INSTEAD NOTHING; o timing on SELECT insert_test_rule(); (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |