PostgreSQL에서 대량 삽입 M : N 관계


9

구식 데이터베이스에서 구조가 약간 다른 새 데이터베이스로 데이터를 가져와야합니다. 예를 들어, 기존 데이터베이스에는 직원과 그 감독자를 기록하는 테이블이 있습니다.

CREATE TABLE employee (ident TEXT PRIMARY KEY, name TEXT, supervisor_name TEXT)

이제 새 데이터베이스는 다음과 같습니다.

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));

즉, 관리자 이름이있는 일반 직원 테이블 대신 새로운 (보다 일반적인) 데이터베이스를 통해 사람들로 구성된 팀을 만들 수 있습니다. 직원들은 역할 구성원 'e', 역할 감독자 's'.

문제는 employee직원-관리자 쌍당 한 팀씩 데이터를 새로운 구조 로 쉽게 마이그레이션하는 방법 입니다. 예를 들어 직원

employee: ('abc01', 'John', 'Dave'), ('abc02', 'Kyle', 'Emily')

로 마이그레이션됩니다

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')

데이터 수정 CTE를 사용하여 직원과 감독자를 먼저 삽입 한 다음 팀을 고려하는 것이 좋습니다. 그러나 CTE는 삽입 된 테이블 행에서만 데이터를 리턴 할 수 있습니다. 따라서 나는 누가 누구의 감독자 였는지 일치시킬 수 없습니다.

내가 볼 수있는 유일한 해결책 plpgsql은 데이터를 반복하고 삽입 된 팀 ID를 임시 변수에 보관 한 다음 적절한 teammember행 을 삽입하는을 사용하는 것 입니다. 그러나 더 단순하거나 더 우아한 솔루션이 있는지 궁금합니다.

대략 수백에서 수천 명의 직원이있을 것입니다. 일반적으로 좋은 방법이지만 내 경우에는 이전 ID가와 같은 문자열이므로 이전 ID를 기반으로 새 ID를 생성하고 싶지 않습니다 *.GM2. old_ident참조 를 위해 열에 저장합니다 .


3
새 테이블에 임시 식별자를 추가하는 것이 좋습니다. 이렇게하면 이전 연결을 유지하면서 데이터를 삽입 할 수 있습니다. 그런 다음 이전 테이블에서 필요한 행을 가져 와서 다음 테이블에 삽입 할 수 있습니다. 이를 위해 별도의 SQL 문을 사용하므로 복잡한 CTE 또는 절차 함수가 필요하지 않습니다.
dezso

@dezso 제안 해 주셔서 감사합니다. team팀을 만든 사람의 ID를 보유 할 임시 식별자를 추가 하면 문제가 해결됩니다. 그래도 더 우아한 (즉, DDL을 사용하지 않는) 솔루션이 있는지 궁금합니다.
Ondřej Bouda 2016 년

@ OndřejBouda CTE 쿼리로 테이블을 빌드하는 것이 가능할 수는 있지만 꽤 복잡해질 수 있습니다. (temp) 테이블 솔루션은 예를 들어 행 수를 확인하여 단계를 개별적으로 테스트하는 고급 기능을 제공합니다.
dezso

답변:


1

이전 데이터베이스에서 새 데이터베이스를 채우는 데 필요한 모든 정보가 4 개의 insert 문으로 구성되어 있습니다.

create table team_ids (id serial, name TEXT)

insert into team_ids (name)
select distinct supervisor_name from employee

-- now supervisors have ids assigned by "serial" type

insert into person (id, name, old_ident)
select ident, name, ident from employee
union
select ident, supervisor_name, ident from employee

insert into team (id) -- meh
select id from team_ids

insert into teammember (person_id, team_id, role)
select e.ident, t.id, 'e')
from employee as e, join team_ids as t
on t.name = e.supervisor_name
union -- and, I guess
select t.id, t.id, 'm')
from team_ids as t

맛에 맞게 조정해야 할 수도 있습니다. 나는 employee.ident가 person.id에 매핑 될 수 있다고 가정하고 DBMS는 자동 생성 값을 가진 열에 값을 할당 할 수 있다고 가정합니다. 그것을 제외하고는 기본 SQL 일뿐이며 환상적이며 물론 루프는 없습니다.

추가 논평 :

  • '팀'테이블의 이름은 (일반적으로) 부서 로 변경 될 수 있습니다 .
  • SERIAL20 억 개의 가능성을 가진 A 는 충분할 것 BIGSERIAL입니다.
  • 관리자에게 1 : 1 카디널리티를 팀에 적용하는 데이터베이스 메커니즘이없는 것 같습니다. 모든 팀에 정의에 따라 리더가 필요한 것은 아닙니까? teammember.role에 대한 CHECK또는 FOREIGN KEY제한이 없습니까? 아마도 질문은 이러한 세부 사항을 단순화했습니다.
  • "teammember"테이블 이름은보다 일반적으로 단어 경계 (예 : TeamMember 또는 team_member)를 갖습니다.

1
이렇게하면 person테이블에 중복 된 ID가 생깁니다 .
dezso

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; $$;
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.