我确定这是常见的地方,但谷歌没有帮助。我试图在PostgreSQL 9.1中编写一个简单的存储过程,它将从父if(true == t.canRecord)
表中删除重复的条目。父表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,
...
);
子句中将其用于将子行从EXCEPTION
移动到一个可接受的键,然后循环回来并从lab
表中删除不必要的记录。
这是(非常错误的)代码:
cpt
答案 0 :(得分:7)
使用带有data-modifying CTEs的单个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;
db&lt;&gt;小提琴here (第11页)
SQL Fiddle(第9.6页)
这应该 更多 更快更清洁。循环比较昂贵,异常处理相对更加昂贵
更重要的是,lab
中的引用会自动重定向到cpt
中的相应主行,但原始代码中却没有。因此,您可以 一次删除所有欺骗 。
如果愿意,您仍然可以将它包装在plpgsql或SQL函数中。
在第一个CTE plan
中,使用相同的cdesc
标识每个分区中的主行。在您的情况下,行具有最小recid
。
在第二次CTE upd_lab
中,将引用欺骗的所有行重定向到cpt
中的主行。
最后,删除dupes,这不会引发异常,因为依赖行实际上同时链接到剩余的主行。
ON DELETE RESTRICT
所有CTE和语句的主要查询都在基础表的同一快照上运行,实际上 并发 。他们没有看到彼此对基础表的影响:
人们可能期望ON DELETE RESTRICT
的FK约束引发异常,因为[每个文档] [3]:
NO ACTION
支票以外的参考操作无法延期, 即使约束被宣布为可延迟的。
但是,上述声明是单一命令和[再次手册] [3]:
将立即检查不可延迟的约束 每个命令。
大胆强调我的。当然,也适用于限制较少的默认ON DELETE NO ACTION
。
但要注意写入相同表的并发事务,但这是一个普遍的考虑因素,并不是特定于此任务。
例外适用于UNIQUE
和PRIMARY KEY
约束,但这与此案例无关:
答案 1 :(得分:1)
您可以选择所有重复项,然后使用记录变量循环结果。 您将可以访问整个当前记录。以下功能可以作为一个例子:
create or replace function show_remove_duplicates_in_cpt ()
returns setof text language plpgsql
as $$
declare
rec record;
begin
for rec in
select * from (
select
recid, cdesc,
row_number() over (partition by cdesc order by recid) as rnum
from cpt
) alias
where rnum > 1
loop
return next format ('fixing foreign key for %s %s %s', rec.recid, rec.cdesc, rec.rnum);
return next format ('deleting from cpt where recid = %s', rec.recid);
end loop;
end $$;
select * from show_remove_duplicates_in_cpt ();