如何阻止Postgres表重复记录,而是将重复记录写入另一个表中。我想将数据从“table1”复制到“table2”,如果“table1”中有任何重复数据,则应将其复制到“duplicates”而不是“table2”。
问题是:
table2
上创建唯一密钥会停止错误插入数据ERROR: duplicate key value violates unique constraint
与可以使用IGNORE命令的MySQL不同。
在table2
上创建规则对每次检查无效。
CREATE RULE "copy_dup" AS ON INSERT TO "table2"
WHERE EXISTS
(SELECT 1 FROM table2 WHERE (field1, field2, field3) =
(NEW.field1, NEW.field2, NEW.field3))
DO INSTEAD INSERT INTO duplicates
(field1, field2, field3)
VALUES
(NEW.field1, NEW.field2, NEW.field3);
INSERT INTO "table2"
(field1, field2, field3)
SELECT
field1, field2, field3 FROM table1;
创建触发器时会出现语法错误:
CREATE FUNCTION data_insert_table2()
RETURNS void AS
$$
BEGIN
INSERT INTO table2(field1, field2, field3)
SELECT field1, field2, field3
FROM table1;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER ignore_dup
BEFORE INSERT ON table2
FOR EACH ROW
WHEN (OLD.* IS DISTINCT FROM NEW.*)
EXECUTE PROCEDURE data_insert_table2();
错误:“WHEN”或附近的语法错误
触发器是否不支持WHEN子句?
我与Postgres不是很亲密。有人可以帮忙吗?
table1
和table2
都有serial_id
的主键,而不是从一个表复制到另一个表。
我不确定此功能是否有效,因为触发器在WHEN
失败。
答案 0 :(得分:4)
您的触发器(至少)因此失败:INSERT
触发器未定义special variable OLD
。没有“旧”行,例如DELETE
或UPDATE
触发器。
否则触发器解决方案应该可以正常工作。基于您更新的问题:
CREATE FUNCTION data_insert_table2()
RETURNS trigger AS
$func$
BEGIN
--
INSERT INTO dupe(field1, field2, field3) -- insert into other table
SELECT NEW.field1, NEW.field2, NEW.field3
WHERE EXISTS ( -- if dupe is already in table2
SELECT 1 FROM table2
WHERE (field1, field2, field3) = (NEW.field1, NEW.field2, NEW.field3)
);
IF FOUND THEN -- only if the above wrote to the dupe table ..
RETURN NULL; -- .. cancel original INSERT
END IF;
RETURN NEW; -- else proceed normally
END;
$func$ LANGUAGE plpgsql;
CREATE TRIGGER ignore_dup
BEFORE INSERT ON table2
FOR EACH ROW -- no WHEN condition!
EXECUTE PROCEDURE data_insert_table2();
对于一次性操作,我会使用data-modifying CTEs进行查询,但是(Postgres 9.1或更高版本):
WITH sel AS (
SELECT t.field1, t.field2, t.field3
FROM (
SELECT DISTINCT ON (field1, field2, field3) -- fold duplicates in source
pk_col, field1, field2, field3
FROM table1
ORDER BY field1, field2, field3, pk_col -- take "first" row per set
) t
LEFT JOIN table2 t2 USING (field1, field2, field3)
WHERE t2.field1 IS NULL -- except rows in table2
)
, ins1 AS (
INSERT INTO table2 (field1, field2, field3)
SELECT field1, field2, field3
FROM sel
)
INSERT INTO dupes (field1, field2, field3)
SELECT t.field1, t.field2, t.field3
FROM table1 t
LEFT JOIN sel USING (pk_col)
WHERE sel.pk_col IS NULL;
将其与UNIQUE
上的table2
约束相结合,以确保满足您的要求。
请注意,保留NULL的两列不相同。如果您不同意此标准SQL定义,则必须定义所有列NOT NULL
才能使其正常工作。
此操作容易出现竞争条件。如果多个客户端可能同时运行此客户端,则需要独占锁。但它看起来像是一个单用户的工作。