【问题标题】:How to combine two records while keeping all referencing records?如何在保留所有引用记录的同时合并两条记录?
【发布时间】:2020-04-23 20:57:35
【问题描述】:

PostgreSQL 11.1

我已经为这个问题苦苦挣扎了很长时间。 (我已经尝试改进之前的问题)。

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

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

在将关联记录保留在表 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;

错误:表“tempa”上的更新或删除违反了表“tempb”上的外键约束“foo_bar_fk” 详细信息:键 (id)=(2) 仍然从表“tempb”中引用

在执行删除之前我似乎无法进行更新。

非常感谢任何帮助。 TIA

【问题讨论】:

    标签: sql postgresql join sql-update sql-delete


    【解决方案1】:

    您需要先update 子表,然后再从父表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

    【讨论】:

    • 随着时间的推移,我预计会有更多像 tempB 这样的表——这些表在编写过程代码(sql)时是未知的。我希望使用外键“级联”机制来“自然地”使这些表得到正确更新。也就是说,我不想每次添加像 tempB 这样的新表时都重写 sql。 (我希望我说的是对的)。谢谢。
    • 在删除或更新之前使用触发器将引用表记录从一个代理键“移动”到另一个时,我想到的唯一一件事是???
    • "Demo on DB Fiddle" 给出:22023ERROR: invalid hexadecimal digit: "="
    • @AlanWayne:外键的级联机制不能在这里做你想做的事情(即,将子记录重新影响到现有父级)...
    • @AlanWayne:在我的经验中并不常见。在正常情况下,您不应该发现自己将孩子的记录重新影响到其他父母......我不确定是否有解决这个问题的通用途径。
    猜你喜欢
    • 1970-01-01
    • 2020-07-07
    • 2021-11-05
    • 2013-04-08
    • 2022-08-16
    • 1970-01-01
    • 2010-11-02
    • 1970-01-01
    • 2021-04-14
    相关资源
    最近更新 更多