Chèn số lượng lớn mối quan hệ M: N trong PostgreSQL


9

Tôi cần nhập dữ liệu từ cơ sở dữ liệu cũ sang cơ sở dữ liệu mới, với cấu trúc hơi khác nhau. Ví dụ, trong cơ sở dữ liệu cũ, có một bảng ghi lại nhân viên và người giám sát của họ:

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

Bây giờ, cơ sở dữ liệu mới như sau:

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

Đó là, thay vì bảng nhân viên đơn giản với tên của người giám sát của họ, cơ sở dữ liệu mới (chung chung hơn) cho phép tạo các nhóm người. Các nhân viên là thành viên có vai trò 'e', giám sát viên với vai trò 's'.

Câu hỏi là làm thế nào để dễ dàng di chuyển dữ liệu từ employeecấu trúc mới, một nhóm cho mỗi cặp nhân viên giám sát. Ví dụ: nhân viên

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

sẽ được di cư như

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

Tôi sẽ xem xét sử dụng CTE sửa đổi dữ liệu, chèn nhân viên và giám sát viên trước, sau đó nhóm giữa họ. Tuy nhiên, CTE chỉ có thể trả về dữ liệu từ hàng của bảng được chèn. Vì vậy, tôi không thể phù hợp với ai là người giám sát của ai.

Giải pháp duy nhất tôi có thể thấy là sử dụng plpgsql, chỉ đơn giản là lặp lại dữ liệu, giữ ID nhóm được chèn trong một biến tạm thời, sau đó chèn các teammemberhàng thích hợp . Nhưng tôi tò mò liệu có những giải pháp đơn giản hơn hay thanh lịch hơn.

Sẽ có khoảng vài trăm đến vài ngàn nhân viên. Mặc dù nói chung đó là một cách thực hành tốt, nhưng trong trường hợp của tôi, tôi không muốn tạo ID mới dựa trên những cái cũ, vì ID cũ là các chuỗi như thế *.GM2. Tôi lưu trữ chúng vào old_identcột để tham khảo.


3
Tôi sẽ đề nghị thêm một số định danh tạm thời vào các bảng mới. Bằng cách này, bạn có thể chèn dữ liệu vào chúng trong khi vẫn có các kết nối cũ - sau đó bạn có thể tìm nạp các hàng cần thiết từ bảng cũ và chèn chúng vào bảng tiếp theo, v.v. Đối với điều này, tôi sẽ sử dụng các câu lệnh SQL riêng biệt, không cần các CTE phức tạp hoặc các hàm thủ tục.
dezso

@dezso Cảm ơn bạn đã gợi ý. Thêm một định danh tạm thời teamsẽ giữ ID của người mà nhóm được tạo sẽ giải quyết vấn đề. Tôi vẫn tò mò liệu có một giải pháp thanh lịch hơn (nghĩa là không sử dụng DDL) hay không.
Ondřej Bouda

@ OndřejBouda có thể xây dựng các bảng dưới dạng truy vấn CTE, nhưng nó có thể trở nên khá phức tạp khá nhanh. Giải pháp bảng (temp) mang đến cho bạn sự sang trọng khi thử nghiệm các bước riêng lẻ, bằng cách kiểm tra số lượng hàng chẳng hạn.
dezso

Câu trả lời:


1

Bạn có tất cả thông tin bạn cần để điền vào cơ sở dữ liệu mới từ cơ sở dữ liệu cũ với 4 câu lệnh chèn:

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

Bạn có thể phải điều chỉnh theo khẩu vị. Tôi giả sử worker.ident có thể được ánh xạ lên person.id và DBMS của bạn cho phép gán giá trị cho các cột với các giá trị được tạo tự động. Ngoại trừ điều đó, nó chỉ là SQL cơ bản, không có gì lạ mắt và tất nhiên , không có vòng lặp.

Bình luận thêm:

  • Bảng 'nhóm' có thể được (được quy ước) đổi tên thành bộ phận .
  • Một SERIAL(với 2 tỷ khả năng của nó) sẽ rất nhiều, không cần a BIGSERIAL.
  • Dường như không có cơ chế cơ sở dữ liệu nào để thực thi quyền số 1: 1 của người quản lý đối với nhóm. Không phải mọi đội đều cần một người lãnh đạo, theo định nghĩa? Không có CHECKhoặc FOREIGN KEYràng buộc cho teammember.role? Có lẽ câu hỏi đơn giản hóa những chi tiết này đi.
  • Tên bảng "teammember" thông thường sẽ có một ranh giới từ, ví dụ TeamMember hoặc team_member.

1
Bằng cách này, bạn sẽ có ID trùng lặp trong personbảng.
dezso

0

PL / PGQuery sẽ thực hiện công việc.

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; $$;
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.