将表拆分为多对多关系:数据迁移

tar*_*aki 12 sql postgresql many-to-many database-migration

我想知道在将表拆分成多对多关系时如何最好地迁移我的数据.我已经做了一个简化的例子,我还会发布一些我想出的解决方案.我正在使用Postgresql数据库.

迁移之前

表人


ID       Name        Pet        PetName
1        Follett     Cat        Garfield
2        Rowling     Hamster    Furry
3        Martin      Cat        Tom
4        Cage        Cat        Tom
Run Code Online (Sandbox Code Playgroud)

迁移后

表人


ID       Name
1        Follett
2        Rowling
3        Martin
4        Cage
Run Code Online (Sandbox Code Playgroud)

表宠物


ID       Pet        PetName
6        Cat        Garfield
7        Hamster    Furry
8        Cat        Tom
9        Cat        Tom
Run Code Online (Sandbox Code Playgroud)

表PersonPet


FK_Person     FK_Pet
1             6
2             7
3             8
4             9
Run Code Online (Sandbox Code Playgroud)

笔记:

  • 我将特别重复宠物表中的条目(因为在我的情况下 - 由于其他相关数据 - 其中一个可能仍然可由客户编辑而另一个可能不可编辑).
  • 没有唯一标识"Pet"记录的列.
  • 对我而言,3-8和4-9是否在PersonPet表或3-9和4-8中链接无关紧要.
  • 此外,我省略了处理表格模式更改的所有代码,因为这是 - 在我的理解中 - 与此问题无关.

我的解决方案

  1. 创建Pet Table时,临时添加一个包含用于创建此条目的Person Table的id的列.

    ALTER TABLE Pet ADD COLUMN IdPerson INTEGER;

    INSERT INTO Pet (Pet, PetName, IdPerson)
    SELECT Pet, PetName, ID
    FROM Person;

    INSERT INTO PersonPet (FK_Person, FK_Pet)
    SELECT ID, IdPerson
    FROM Pet;

    ALTER TABLE Pet DROP Column IdPerson;
Run Code Online (Sandbox Code Playgroud)
  1. 避免临时修改Pet表

    INSERT INTO Pet (Pet, PetName)
    SELECT Pet, PetName
    FROM Person;

    WITH
      CTE_Person
      AS
      (SELECT
        Id, Pet, PetName
        ,ROW_NUMBER() OVER (PARTITION BY Pet, PetName ORDER BY Id) AS row_number
      FROM Person
      )
      ,CTE_Pet
      AS
      (SELECT
        Id, Pet, PetName
        ,ROW_NUMBER() OVER (PARTITION BY Pet, PetName ORDER BY Id) AS row_number
      FROM Pet
      )
      ,CTE_Joined
      AS
      (SELECT
        CTE_Person.Id AS Person_Id,
        CTE_Pet.Id AS Pet_Id
      FROM
        CTE_Person
        INNER JOIN CTE_Pet ON
        CTE_Person.Pet = CTE_Pet.Pet
        CTE_Person.PetName = CTE_Pet.PetName
        AND CTE_Person.row_number = CTE_Pet.row_number
      )
      INSERT INTO PersonPet (FK_Person, FK_Pet)
      SELECT Person_Id, Pet_Id from CTE_Joined;
Run Code Online (Sandbox Code Playgroud)

问题

  1. 两种解决方案都正确吗 (我测试了第二个解决方案,结果似乎是正确的,但我可能错过了一些角落案例)
  2. 这两种解决方案的优点/缺点是什么?
  3. 有没有更简单的方法进行相同的数据迁移?(为了我的好奇心,我也会对稍微修改我的约束的答案感兴趣(例如Pet表中没有重复的条目),但请指出哪些:)).

Rad*_*icz 5

实现您所描述的效果的另一种解决方案(在我看来是最简单的一种;没有任何CTE-s或其他列):

create table Pet as
    select
        Id,
        Pet,
        PetName
    from 
        Person;

create table PersonPet as
    select
        Id as FK_Person,
        Id as FK_Pet
    from
        Person;

create sequence PetSeq;
update PersonPet set FK_Pet=nextval('PetSeq'::regclass);
update Pet p set Id=FK_Pet from PersonPet pp where p.Id=pp.FK_Person;

alter table Pet alter column Id set default nextval('PetSeq'::regclass);
alter table Pet add constraint PK_Pet primary key (Id);
alter table PersonPet add constraint FK_Pet foreign key (FK_Pet) references Pet(Id);
Run Code Online (Sandbox Code Playgroud)

除非我们使用序列生成一个id,否则我们只是使用现有的person id作为宠物的临时id.

编辑

也可以使用我已经完成模式更改的方法:

insert into Pet(Id, Pet, PetName)
    select
        Id,
        Pet,
        PetName
    from
        Person;

insert into PersonPet(FK_Person, FK_Pet)
    select
        Id,
        Id
    from
        Person;

select setval('PetSeq'::regclass, (select max(Id) from Person));
Run Code Online (Sandbox Code Playgroud)