Можете да направите това много по-ефективно с един SQL оператор с CTE, модифициращи даннисилен> .
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<>fiddle тук
(стр. 11)
SQL Fiddle
(стр. 9.6)
Това трябва да емного по-бързо и по-чисто. Цикълът е сравнително скъп, обработката на изключения е сравнително още по-скъпа.
По-важното е, че препратките в lab
се пренасочват към съответния главен ред в cpt
автоматично, което все още не е във вашия оригинален код. Така че можете да изтриете всички дублирания наведнъж .
Все още можете да обвиете това в plpgsql или SQL функция, ако желаете.
Обяснение
-
В 1-ви CTE
plan
, идентифицирайте главен ред във всеки дял със същияcdesc
. Във вашия случай редът с минималнияrecid
. -
Във 2-ри CTE
upd_lab
пренасочва всички редове, препращащи към дублиране, към главния ред вcpt
. -
И накрая, изтрийте дупки, което няма да доведе до изключения, тъй като зависещите редове се свързват с оставащия главен ред почти по същото време.
ON DELETE RESTRICT
Всички CTE и основната заявка на изявление работят върху една и съща моментна снимка на базови таблици, практически едновременно . Те не виждат ефектите на другия върху основните таблици:
Може да се очаква FK ограничение с ON DELETE RESTRICT
да предизвика изключения, защото [по документация][3]:
Горният израз обаче е една команда и [отново ръководството][3]:
Удебелен акцент мой. Работи за по-малко рестриктивното по подразбиране ON DELETE NO ACTION
също, разбира се.
Но внимавайте с едновременни транзакции, записващи в едни и същи таблици, но това е общо съображение, а не специфично за тази задача.
Важи изключение за UNIQUE
и PRIMARY KEY
ограничение, но това не засяга това case: