如何删除具有外键依赖项的重复行?

Ala*_*yne 4 database postgresql exception-handling foreign-keys plpgsql

我确信这是常见的地方,但谷歌没有帮助.我试图在PostgreSQL 9.1中编写一个简单的存储过程,它将从父cpt表中删除重复的条目.父表cpt由子表引用,lab定义为:

CREATE TABLE lab (
 recid serial NOT NULL,
 cpt_recid integer,
  ........
 CONSTRAINT cs_cpt FOREIGN KEY (cpt_recid)
   REFERENCES cpt (recid) MATCH SIMPLE
   ON UPDATE NO ACTION ON DELETE RESTRICT,
 ...
);
Run Code Online (Sandbox Code Playgroud)

我遇到的最大问题是如何获取失败的记录,以便我可以在EXCEPTION子句中使用它将子行移动lab到一个可接受的键,然后循环返回并从cpt表中删除不必要的记录.

这是(非常错误的)代码:

CREATE OR REPLACE FUNCTION h_RemoveDuplicateCPT()
  RETURNS void AS
$BODY$
BEGIN
LOOP
   BEGIN

   DELETE FROM cpt
   WHERE recid IN (
      SELECT recid
      FROM  (
         SELECT recid,
         row_number() over (partition BY cdesc ORDER BY recid) AS rnum
         FROM cpt) t
      WHERE t.rnum > 1)
   RETURNING recid;

   IF count = 0 THEN
      RETURN;
   END IF;  

   EXCEPTION WHEN foreign_key_violation THEN
      RAISE NOTICE 'fixing unique_violation';
      RAISE NOTICE 'recid is %' , recid;
   END;
END LOOP;
END;                    
$BODY$
LANGUAGE plpgsql VOLATILE;
Run Code Online (Sandbox Code Playgroud)

Erw*_*ter 8

使用具有数据修改CTE单个SQL语句,您可以更有效地执行此操作.

WITH plan AS (
   SELECT *
   FROM  (
      SELECT recid, min(recid) OVER (PARTITION BY cdesc) AS master_recid
      FROM   cpt
      ) sub
   WHERE  recid <> master_recid  -- ... <> self
   )
 , upd_lab AS (
   UPDATE lab l
   SET    cpt_recid = p.master_recid   -- link to master recid ...
   FROM   plan p
   WHERE  l.cpt_recid = p.recid
   )
DELETE FROM cpt c
USING  plan p
WHERE  c.recid = p.recid
RETURNING c.recid;
Run Code Online (Sandbox Code Playgroud)

db <> 在这里小提琴(第11页)
SQL小提琴(第9.6页)

这应该是很多更快和更清洁.循环比较昂贵,异常处理相对更昂贵.
更重要的是,引用lab会自动重定向到相应的主行cpt,但原始代码中却没有.所以你可以一次删除所有欺骗.

如果愿意,您仍然可以将它包装在plpgsql或SQL函数中.

说明

  1. 在第一个CTE中plan,使用相同的方法在每个分区中标识主行cdesc.在你的情况下,行最小recid.

  2. 在第二个CTE中,upd_lab将引用欺骗的所有行重定向到主行中cpt.

  3. 最后,删除dupes,这不会引发异常,因为依赖行实际上同时链接到剩余的主行.

ON DELETE RESTRICT

所有CTE和语句的主要查询几乎同时在基础表的相同快照上运行.他们没有看到彼此对基础表的影响:

人们可能期望FK约束ON DELETE RESTRICT引发异常,因为[按文档] [3]:

NO ACTION即使约束被声明为可延迟,也不能推迟除检查之外的引用操作.

但是,上面的语句是单个命令,[再次手册] [3]:

在每个命令之后立即检查不可延迟的约束.

大胆强调我的.ON DELETE NO ACTION当然,也适用于限制较少的默认值.

但是要警惕写入相同表的并发事务,但这是一个普遍的考虑因素,不是特定于此任务的.

异常适用于UNIQUEPRIMARY KEY约束,但这不涉及情况: