古いデータベースのデータを、構造が少し異なる新しいデータベースにインポートする必要があります。たとえば、古いデータベースには、従業員とその上司を記録するテーブルがあります。
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
から新しい構造に、従業員と監督者のペアごとに1つのチームにデータを簡単に移行する方法です。たとえば、従業員
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は*.GM2
のような文字列であるため、一般的には良い方法ですが、私の場合、古いIDに基づいて新しいIDを生成したくありません。参照用にold_ident
列に保存します。
次の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
好みに合わせて調整する必要があるかもしれません。ここでは、employee.identをperson.idにマップでき、DBMSで値が自動生成された列に割り当てられると想定しています。それを除いて、それは基本的なSQLであり、空想的ではなく、そしてもちろん、ループはありません。
追加の解説:
SERIAL
(20億の可能性がある)は十分なはずです。BIGSERIAL
は必要ありません。CHECK
またはFOREIGN KEY
制約はありませんか?おそらく、質問によってこれらの詳細が単純化されたのでしょう。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; $$;