การแทรก M: ความสัมพันธ์จำนวนมากใน PostgreSQL


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แถวที่เหมาะสม แต่ฉันสงสัยว่ามีวิธีแก้ปัญหาที่ง่ายกว่าหรือหรูหรากว่านี้หรือไม่

จะมีพนักงานประมาณหลายร้อยถึงหลายพันคน *.GM2แม้ว่ามันจะเป็นโดยทั่วไปการปฏิบัติที่ดีในกรณีของฉันฉันจะไม่เหมือนการสร้างรหัสใหม่บนพื้นฐานของคนเก่าเป็นรหัสเก่าสตริงเช่น ฉันเก็บมันไว้ในold_identคอลัมน์เพื่ออ้างอิง


3
ฉันขอแนะนำให้เพิ่มตัวระบุชั่วคราวลงในตารางใหม่ วิธีนี้คุณสามารถแทรกข้อมูลลงในพวกเขาในขณะที่ยังคงมีการเชื่อมต่อเก่า - จากนั้นคุณสามารถดึงข้อมูลแถวที่จำเป็นจากตารางเก่าและแทรกลงในตารางถัดไปเป็นต้น สำหรับเรื่องนี้ฉันจะใช้คำสั่ง SQL แยกต่างหากไม่จำเป็นต้องใช้ CTE ที่ซับซ้อนหรือฟังก์ชั่นขั้นตอน
dezso

@dezso ขอบคุณสำหรับคำแนะนำ การเพิ่มตัวระบุชั่วคราวteamซึ่งจะถือ ID ของบุคคลที่ทีมถูกสร้างขึ้นจะช่วยแก้ปัญหาได้ ฉันยังคงสงสัยว่ามีวิธีแก้ปัญหาที่สง่างามกว่า (เช่นใช้ DDL) หรือไม่
Ondřej Bouda

@ OndřejBoudaมันอาจเป็นไปได้ที่จะสร้างตารางเป็นแบบสอบถาม CTE แต่มันอาจจะค่อนข้างซับซ้อนอย่างรวดเร็ว โซลูชันตาราง (temp) ให้ความหรูหราในการทดสอบแต่ละขั้นตอนโดยการตรวจสอบการนับจำนวนแถว
dezso

คำตอบ:


1

คุณมีข้อมูลทั้งหมดที่คุณต้องการเติมฐานข้อมูลใหม่จากฐานข้อมูลเก่าที่มีคำสั่งแทรก 4 รายการ:

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

คุณอาจต้องปรับตัวเพื่อลิ้มรส ฉันสมมติว่า staff.ident สามารถจับคู่กับ person.id และ DBMS ของคุณอนุญาตให้กำหนดค่าให้กับคอลัมน์ด้วยค่าที่สร้างขึ้นอัตโนมัติ ยกเว้นว่าเป็นเพียง SQL พื้นฐานไม่มีอะไรแฟนซีและแน่นอนไม่มีลูป

ความเห็นเพิ่มเติม:

  • ของทีมโต๊ะอาจจะ (ตามอัตภาพ) เปลี่ยนชื่อเป็นกรม
  • SERIAL(มี 2 พันล้านเป็นไปได้ของมัน) BIGSERIALควรจะมีความอุดมสมบูรณ์จำเป็นที่จะต้องไม่มี
  • ดูเหมือนจะไม่มีกลไกฐานข้อมูลในการบังคับใช้ 1: 1 cardinality ของผู้จัดการทีม ทุกทีมไม่จำเป็นต้องมีผู้นำหรือไม่? ไม่มีCHECKหรือFOREIGN KEYข้อ จำกัด สำหรับ teammember.role? บางทีคำถามอาจทำให้รายละเอียดเหล่านี้ง่ายขึ้น
  • ชื่อตาราง "teammember" จะมีขอบเขตของคำโดยทั่วไปเช่น TeamMember หรือ team_member

1
วิธีนี้คุณจะมีรหัสซ้ำกันในpersonตาราง
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.