如何在保留所有引用记录的同时合并两个记录?

How to combine two records while keeping all referencing records?

PostgreSQL 11.1

这个问题我纠结了很久。 (我已经尝试改进之前的问题)。

问题:一个人在 table tempA 中有两个不同的名字。每个名称在 table tempB 中都有其相关记录。如何将与一个姓名关联的所有记录移动到另一个姓名,然后删除该姓名?

示例:我有两个名字 --"Tom" 和 "Bob"。我想将与 "Bob" 关联的所有记录更改为 "Tom",然后从数据库中删除 "Bob"。

如何在 table tempb 中保留相关记录的同时做到这一点?

CREATE TABLE tempA
(
    id serial PRIMARY KEY,
    name text UNIQUE NOT NULL
);


CREATE TABLE tempb
(
    id serial PRIMARY KEY,
    tempa_id integer NOT NULL,
    description text NOT NULL,
    CONSTRAINT foo_bar_fk FOREIGN KEY (tempa_id)
        REFERENCES tempa (id) MATCH SIMPLE
        ON UPDATE CASCADE
        ON DELETE NO ACTION
        DEFERRABLE INITIALLY DEFERRED
)

INSERT INTO tempA (name) VALUES('tom');
INSERT INTO tempA (name) VALUES('bob');

INSERT INTO tempB (tempA_id, description) SELECT id, 'test1' FROM tempA WHERE tempA.name = 'tom';
INSERT INTO tempB (tempA_id, description) SELECT id, 'test2' FROM tempA WHERE tempA.name = 'tom';
INSERT INTO tempB (tempA_id, description) SELECT id, 'test3' FROM tempA WHERE tempA.name = 'bob';
INSERT INTO tempB (tempA_id, description) SELECT id, 'test4' FROM tempA WHERE tempA.name = 'bob';

Initial set:
-- tempA
id  name
1   "tom"
2   "bob"

id tempA_id  description
1   1      "test1"
2   1      "test2"
3   2      "test3"
4   2      "test4"

我想要达到的目标是:

--Desired Results
-- tempA
id  name
1   "tom"

-- tempB
id tempA_id  description
1   1       "test1"
2   1       "test2"
3   1       "test3"
4   1       "test4"

这是我尝试过的方法,但仍然失败:

BEGIN;

SET CONSTRAINTS ALL  DEFERRED; 

-- from 'tom' to 'bob' -- when all is done 'tom' must be the name to keep.
WITH _in (name1, name2) AS(
        VALUES('tom','bob')
),
 _bob  AS(                 -- DELETING 'bob' record FROM tempA. 
        DELETE FROM tempA                     
        USING _in 
        WHERE (tempA.name = _in.name2)
        RETURNING tempA.*
)
UPDATE tempA        -- REPLACING 'bob' with 'tom'. REPLACING 'bobs' id with 'toms' id.
SET name = _in.name1, id = _tom.id
FROM _in 
JOIN _bob ON (_bob.name = _in.name2)
JOIN tempA _tom ON (_tom.name = _in.name1)
WHERE (tempA.id = _bob.id);

COMMIT;

错误:table "tempa" 上的更新或删除违反了 table "tempb" 上的外键约束 "foo_bar_fk" 详细信息:Key (id)=(2) 仍然引用自 table "tempb".

看来我无法在执行删除之前进行更新。

非常感谢任何帮助。 TIA

您需要先 update 子 table,然后 delete 来自父 - 外键约束阻止您以相反的方式进行。

考虑:

with 
    names (to_keep, to_del) as(values('tom','bob')),
    upd as (
        update tempB
        set tempA_id = a_keep.id
        from names n
        inner join tempA a_keep on a_keep.name = n.to_keep
        inner join tempA a_del  on a_del.name  = n.to_del
        where tempB.tempA_id = a_del.id
        returning a_del.id
    )
delete from tempA 
using upd
where tempA.id = upd.id

Demo on DB Fiddle