PostgreSQL'de toplu ekleme M: N ilişkisi


9

Biraz farklı bir yapıya sahip eski bir veritabanından yeni bir veri aktarmak gerekiyor. Örneğin, eski veritabanında, çalışanları ve amirlerini kaydeden bir tablo vardır:

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

Şimdi, yeni veritabanı aşağıdaki gibidir:

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

Yani, amirlerinin isimleriyle düz çalışan tablosu yerine, yeni (daha genel) veritabanı, insan ekipleri oluşturmayı sağlar. Çalışanlar rol oynayan üyeler, rol 'e'süpervizörleridir 's'.

Soru, verilerin employeeher çalışan-yönetici çifti başına bir ekip olmak üzere yeni yapıya nasıl kolayca taşınacağıdır . Örneğin, çalışanlar

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

olarak taşınacaklar

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

Önce veri değiştiren bir CTE kullanmayı, önce çalışanları ve amirleri, sonra da ekipleri yerleştirmeyi düşünürdüm. Ancak, CTE yalnızca eklenen tablo satırından veri döndürebilir. Bu yüzden kimin kimin süpervizörü olduğunu eşleştiremiyorum.

Görebildiğim tek çözüm plpgsql, verileri tekrarlamak, eklenen takım kimliklerini geçici bir değişkente tutmak ve daha sonra uygun teammembersatırları eklemek . Ancak daha basit veya daha zarif çözümler olup olmadığını merak ediyorum.

Kabaca birkaç yüzlerce ila binlerce çalışan olacak. Genellikle iyi bir uygulama olmasına rağmen, benim durumumda, eski kimlikler gibi dizeler olduğu için eski kimliklere dayalı yeni kimlikler oluşturmak istemiyorum *.GM2. Onları old_identreferans için sütuna saklıyorum.


3
Yeni tablolara bazı geçici tanımlayıcılar eklemenizi öneririm. Bu şekilde, eski bağlantılara sahipken bunlara veri ekleyebilirsiniz - daha sonra eski tablodan gerekli satırları alabilir ve bir sonraki tabloya vb. Ekleyebilirsiniz. Bunun için, ayrı SQL ifadeleri kullanacağım, karmaşık CTE'lere veya prosedürel fonksiyonlara gerek yok.
dezso

@dezso Öneri için teşekkürler. teamEkibin oluşturulduğu kişinin kimliğini tutacak geçici bir tanımlayıcı eklemek sorunu çözecektir. Yine de daha şık (DDL kullanmadan) bir çözüm olup olmadığını merak ediyorum.
Ondřej Bouda

@ OndřejBouda, tabloları CTE sorguları olarak oluşturmak mümkün olabilir, ancak oldukça hızlı bir şekilde karmaşık olabilir. (Temp) tablo çözümü, örneğin satır sayılarını kontrol ederek adımları tek tek test etme lüksünü verir.
dezso

Yanıtlar:


1

Yeni veritabanını eskisinden 4 insert deyimiyle doldurmak için ihtiyacınız olan tüm bilgilere sahipsiniz:

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

Zevkinize göre ayarlamanız gerekebilir. Ben çalışan.ident kişi.id üzerine eşlenebilir ve DBMS otomatik oluşturulan değerleri olan sütunlara değer atama izin varsayalım. Bunun dışında, sadece temel SQL, fantezi bir şey ve tabii ki döngüler yok.

Ek yorum:

  • 'Takım' tablosu (daha geleneksel olarak) bölüm olarak yeniden adlandırılabilir .
  • A SERIAL(2 milyar olasılıkla) bol olmalı, a gerek yok BIGSERIAL.
  • Yöneticinin takıma 1: 1 önemini zorlayan hiçbir veritabanı mekanizması yok gibi görünüyor. Her takımın tanımı gereği bir lidere ihtiyacı yok mu? Bir yok mudur CHECKveya FOREIGN KEYkısıtlama teammember.role için? Belki de soru bu detayları basitleştirdi.
  • "Takım üyesi" tablo adının daha geleneksel bir kelime sınırı vardır, örneğin TeamMember veya team_member.

1
Bu şekilde persontabloda yinelenen kimlikler olur .
dezso

0

PL / PgSQL işi yapacak.

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; $$;
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.