【问题标题】:How can I stop a postgres table from having duplicate record and instead writing the duplicate records in another table如何阻止 postgres 表具有重复记录,而是将重复记录写入另一个表
【发布时间】:2013-11-29 13:41:35
【问题描述】:

如何阻止 Postgres 表出现重复记录,而将重复记录写入另一个表。我想将数据从“table1”复制到“table2”,如果“table1”中有任何重复数据,则应将其复制到“duplicates”而不是“table2”。

问题是:

  1. table2 上创建唯一键会停止数据插入并出现错误
ERROR:  duplicate key value violates unique constraint

与 MySQL 不同,可以使用 IGNORE 命令。

  1. 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;
    
  2. 创建触发器会在以下情况出现语法错误:

    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 不是很熟悉。有人可以帮忙吗?

table1table2 在其 serial_id 上都有主键,不会从一个表复制到另一个表。

我不确定这个函数是否在WHEN 触发失败时起作用。

【问题讨论】:

  • 您的data_insert_table2() 的触发函数定义丢失。
  • @ErwinBrandstetter 如果它只到达WHEN 然后肯定会因语法错误而轰炸,这不会有很大的可能性吗?
  • 此操作是否需要以某种方式按需重复?因为如果不是,您可以制作一对SELECT 查询,选择a)每组重复项中的第一个(包括不重复的行),以及b)每组重复项中的第一个。 (注意asking for help with a possibly non-optimal solution rather than the original problem。)
  • @IMSoP:要获得完整的答案,我需要这些详细信息。手头的错误是微不足道的(在INSERT 触发器中使用OLD)...
  • 另外,table1中是否有主键?

标签: sql postgresql duplicates postgresql-9.1


【解决方案1】:

问题

您的触发器失败(至少)是因为这个原因:special variable OLD 没有为 INSERT 触发器定义。没有“旧”行,例如在 DELETEUPDATE 触发器中。

带触发器的永久解决方案

否则,触发器解决方案应该可以正常工作。基于您更新的问题:

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;

将其与table2 上的UNIQUE 约束相结合,以确保满足您的要求。

请注意,持有 NULL 的两列不被视为相同。如果您不同意此标准 SQL 定义,则必须定义所有列 NOT NULL 以使其正常工作。

此操作容易出现竞争条件。如果多个客户端可能同时运行,则需要独占锁。但它看起来像是单用户作业。

【讨论】:

  • 感谢您的解决方案。工作得很好。
  • 带触发器的永久解决方案在创建函数时出错。 “错误:新用于不在规则中的查询”。我可以通过替换“RETURNS void AS”->“RETURNS TRIGGER AS”来消除这个错误。否则这个查询是完美的。再次感谢。
  • @nitxx:当然,已修复。触发器需要RETURNS trigger。复制您的原件时引起了我的注意。
猜你喜欢
  • 1970-01-01
  • 2012-03-23
  • 2020-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-09-14
相关资源
最近更新 更多