postgresql – 忽略重复插入的最佳方法?

前端之家收集整理的这篇文章主要介绍了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;

问题

我的问题主要是学术问题:

>哪种方法最有效?
>哪种方法最易维护,为什么?
>使用Postgresql忽略插入重复错误的标准方法是什么?
>是否存在技术上更有效的方法来忽略重复插入;如果是的话,它是什么?

谢谢!

作为另一个问题的答案(其中一个被认为是重复的)提及,(从版本9.5开始)存在本机 UPSERT功能.对于旧版本,请继续阅读:)

我已经设置了检查选项的测试.我将包含下面的代码,它可以在linux / Unix机器上的psql中运行(仅仅因为为了结果清晰起见,我将设置命令的输出传送到/ dev / null – 在Windows机器上可以选择一个日志文件).

我试图通过在plpgsql存储过程中使用一个循环来运行每种类型多个(即100个)INSERT来使不同的结果具有可比性.此外,在每次运行之前,通过截断并重新插入原始数据来重置表.

检查一些测试运行,看起来像使用规则并显式添加WHERE NOT EXISTS INSERT语句花费相似的时间,而导致异常需要花费更多的时间才能完成.

后者不是surprising

Tip: 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.

就个人而言,由于可读性和可维护性,我更喜欢将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();

猜你在找的Postgre SQL相关文章