Dav*_*vis 35 postgresql constraint insert duplication unique-constraint
此问题与使用 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
);
Run Code Online (Sandbox Code Playgroud)
代码不受检查现有值的影响。(在这种特殊情况下,用户并不关心插入重复项的错误——插入应该“正常工作”。)在这种情况下添加代码来显式测试重复项会带来复杂性。
在 PostgreSQL 中,我找到了几种忽略重复插入的方法。
创建一个捕获唯一约束违规的事务,不采取任何行动:
BEGIN
INSERT INTO db_table (tbl_column) VALUES (v_tbl_column);
EXCEPTION WHEN unique_violation THEN
-- Ignore duplicate inserts.
END;
Run Code Online (Sandbox Code Playgroud)
创建规则以忽略给定表上的重复项:
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;
Run Code Online (Sandbox Code Playgroud)
我的问题主要是学术性的:
谢谢!
dez*_*zso 23
正如对另一个问题(这个问题被认为是重复的)的回答所提到的,有(从 9.5 版开始)一个本机UPSERT功能。对于旧版本,请继续阅读:)
我已经设置了一个测试来检查选项。我将包括下面的代码,它可以在psqllinux/Unix 机器上运行(仅仅是因为为了结果的清晰起见,我将设置命令的输出通过管道传输到/dev/null- 在 Windows 机器上,可以选择一个日志文件代替)。
我试图通过对INSERT每种类型使用多个(即 100 个),从plpgsql存储过程中的循环运行来使不同的结果具有可比性。此外,在每次运行之前,通过截断并重新插入原始数据来重置表。
检查一些测试运行,看起来使用规则并显式添加WHERE NOT EXISTSotINSERT语句花费相似的时间,而捕获异常需要更多的时间来完成。
后者并不令人惊讶:
提示:包含 EXCEPTION 子句的块比没有 EXCEPTION 子句的块进入和退出的成本要高得多。因此,不要在不需要的情况下使用 EXCEPTION。
就个人而言,由于可读性和可维护性,我更喜欢将WHERE NOT EXISTS位添加到INSERTs 本身。就像触发器(也可以在这里测试)一样,调试(或简单地跟踪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,
col1 double precision,
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,
col1 double precision,
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, rnd, 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, rnd, 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();
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
90832 次 |
| 最近记录: |