如何合并表上的行并更新Postgres上的联结表

时间:2019-06-05 10:53:49

标签: postgresql merge sql-update junction-table

考虑具有多对多关系的2个表(var app = new Vue({ el: "#app", data: function() { return { headers: ['Nº', 'Name', 'Gender'], items: [{}], } }, methods: { add: function(item) { this.items.push(item); } }, mounted: function() { this.add({no:'1',name:'Tom Tom',gender:'male'}); this.add({no:'2',name:'Marcos Huck',gender:'male'}); }, }); table A),每个表包含一个主键和其他属性。为了映射此关系,存在第三个联合表(table B),其中包含关系的每个表(table C的外键。

fk_tableA | fk_tableB包含重复的行(除了pk),所以我想使用唯一的主键将它们合并到一条记录中,就像这样:

Table B

通过合并这些记录,可能有table B table B (after merging duplicates) 1 | Henry | 100.0 1 | Henry | 100.0 2 | Jessi | 97.0 2 | Jessi | 97.0 3 | Henry | 100.0 4 | Erica | 11.2 4 | Erica | 11.2 (联合表)的外键指向不再存在的table C主键。我的目标是编辑它们以指向合并的记录:

合并之前:

table B

tableA table B table C id | att1 id | att1 | att2 fk_A | fk_b ----------- ------------------- ------------ 1 | ab123 1 | Henry | 100.0 1 | 1 2 | adawd 2 | Jessi | 97.0 2 | 3 3 | da3wf 3 | Henry | 100.0 4 | Erica | 11.2 上,引用了table C中的2条记录(1和3),它们恰好是重复的行。我的目标是将这些合并到一个记录中(在table B中),并更新table B中的外键:

合并后:

table C

所以我的问题基本上是 如何在合并表记录时更新联结表? 我目前正在使用Postgres并处理数百万个数据。

1 个答案:

答案 0 :(得分:1)

-- \i tmp.sql

CREATE TABLE persons
        ( id integer primary key
        , name text
        , weight decimal(4,1)
        );

INSERT INTO persons(id,name,weight)VALUES
 (1 ,'Henry', 100.0)
 ,(2 ,'Jessi', 97.0)
 ,(3 ,'Henry', 100.0)
 ,(4 ,'Erica', 11.)
        ;

CREATE TABLE junctiontab
        ( fk_A integer NOT NULL
        , p_id integer REFERENCES persons(id)
        , PRIMARY KEY (fk_A,p_id)
         );

INSERT INTO junctiontab(fk_A, p_id)VALUES (1 , 1 ),(2 , 3  );

        -- find the ids of affected persons.
        -- [for simplicity: put them in a temp table]
CREATE TEMP table xlat AS
SELECT * FROM(
        SELECt id AS wrong_id
                ,min(id) OVER (PARTITION BY name ORDER BY id) AS good_id
        FROM persons p
        ) x
WHERE good_id <> wrong_id
        ;

        --show it
SELECT *FROM xlat;

UPDATE junctiontab j
SET p_id = x.good_id
FROM xlat x
WHERE j.p_id = x.wrong_id
        -- The good junction-entry *could* already exist...
AND NOT EXISTS (
        SELECT *FROM junctiontab nx
        WHERE nx.fk_A= j.fk_A
        AND nx.p_id= x.good_id
        )
        ;

DELETE FROM junctiontab d
        -- if the good junction-entry already existed, we can delete the wrong one now.
WHERE EXISTS (
        SELECT *FROM junctiontab g
        JOIN xlat x ON g.p_id= x.good_id
                   AND d.p_id = x.wrong_id
        WHERE g.fk_A= d.fk_A
        )
        ;

        --show it
SELECT *FROM junctiontab
        ;

        -- Delete thewrongperson-records
DELETE FROM persons p
WHERE EXISTS (
        SELECT *FROM xlat x
        WHERE p.id = x.wrong_id
        );

        --show it
SELECT * FROM persons p;

结果:


DROP SCHEMA
CREATE SCHEMA
SET
CREATE TABLE
INSERT 0 4
CREATE TABLE
INSERT 0 2
SELECT 1
 wrong_id | good_id 
----------+---------
        3 |       1
(1 row)

UPDATE 1
DELETE 0
 fk_a | p_id 
------+------
    1 |    1
    2 |    1
(2 rows)

DELETE 1
 id | name  | weight 
----+-------+--------
  1 | Henry |  100.0
  2 | Jessi |   97.0
  4 | Erica |   11.0
(3 rows)