在 PostgreSQL 中批量插入 M:N 关系

Ond*_*uda 9 postgresql migration

我需要将数据从旧数据库导入新数据库,但结构略有不同。例如,在旧数据库中,有一个表记录了员工及其主管:

CREATE TABLE employee (ident TEXT PRIMARY KEY, name TEXT, supervisor_name TEXT)
Run Code Online (Sandbox Code Playgroud)

现在,新的数据库如下:

CREATE TABLE person (id BIGSERIAL PRIMARY KEY, name TEXT, old_ident TEXT);
CREATE TABLE team (id BIGSERIAL PRIMARY KEY);
CREATE TABLE teammember (person_id BIGINT, team_id BIGINT, role CHAR(1));
Run Code Online (Sandbox Code Playgroud)

也就是说,新的(更通用的)数据库可以创建人员团队,而不是带有主管姓名的普通员工表。员工是有角色的成员,有角色的'e'主管's'

问题是如何轻松地将数据employee从新结构迁移到新结构,每个员工-主管对一个团队。例如,员工

employee: ('abc01', 'John', 'Dave'), ('abc02', 'Kyle', 'Emily')
Run Code Online (Sandbox Code Playgroud)

将被迁移为

person: (1, 'John', 'abc01'), (2, 'Dave', NULL), (3, 'Kyle', 'abc02'), (4, 'Emily', NULL)
team: (1), (2)
teammember: (1, 1, 'e'), (2, 1, 's'), (3, 2, 'e'), (4, 2, 's')
Run Code Online (Sandbox Code Playgroud)

我会考虑使用数据修改 CTE,首先插入员工和主管,然后插入其中的团队。但是,CTE 可能只从插入的表行返回数据。因此,我无法匹配谁是谁的主管。

我能看到的唯一解决方案是使用plpgsql,它会简单地遍历数据,将插入的团队 ID 保存在临时变量中,然后插入适当的teammember行。但我很好奇是否有更简单或更优雅的解决方案。

大约有数百到数千名员工。虽然这通常是一个很好的做法,但在我的情况下,我不想根据旧 ID 生成新 ID,因为旧 ID 是像*.GM2. 我将它们存储到old_ident列中以供参考。

fil*_*rem 0

PL/PgSQL 可以完成这项工作。

DO $$
DECLARE
  _e record;
  _personid bigint;
  _suppersonid bigint;
  _teamid bigint;
BEGIN
  FOR _e IN
    SELECT ident, name, supervisor_name FROM employee
  LOOP
    -- insert person record for employee
    INSERT INTO person (name, old_ident)
      SELECT _e.name, _e.ident
      RETURNING id INTO _personid;
    -- lookup or insert person record for supervisor
    SELECT id INTO _suppersonid FROM person
      WHERE p.name = _e.supervisor_name;
    IF _suppersonid IS NULL THEN
      INSERT INTO person (name) SELECT _e.supervisor_name
        RETURNING id INTO _suppersonid;
    END IF;
    -- lookup team by supervisor or insert new team
    SELECT team_id INTO _teamid FROM teammember tm
      WHERE tm.person_id = _suppersonid AND tm.role = 's';
    IF _teamid IS NULL THEN
      -- new supervisor: insert new team and supervisor
      INSERT INTO team (id) VALUES(DEFAULT) RETURNING id INTO _teamid;
      INSERT INTO teammember (person_id, team_id, role) SELECT _suppersonid, _teamid, 's';
    END IF;
    -- insert team member (non-supervisor) record
    INSERT INTO teammember (person_id, team_id, role) SELECT _personid, _teamid, 'e';
  END LOOP;
END; $$;
Run Code Online (Sandbox Code Playgroud)