Home > database >  How do I move data from one table to another and update references?
How do I move data from one table to another and update references?

Time:12-16

I have a table:

select * from users
id | name |   company_name
 1 |  Sam | Sam's Plumbing
 2 |  Pat |   Pat's Bakery
 3 |  Vic |

I want to move users.company_name to a new companies table, with users.company_id referencing companies.id. Preferably, I'd like to do this in one transaction.

This expresses what I want conceptually, but isn't valid SQL:

BEGIN;

-- 1: add companies
CREATE TABLE companies (
  id integer PRIMARY KEY GENERATED BY DEFAULT AS IDENTITY,
  name varchar(255) not null
);

-- 2: add users.company_id -> companies.id
ALTER TABLE users
ADD COLUMN company_id INT
CONSTRAINT users_company_id_fk REFERENCES companies (id);

-- 3: move users.company_name to companies.name; update FK
UPDATE users
SET users.company_id = inserted_companies.id
FROM (
  INSERT INTO companies (name)
  SELECT company_name FROM users
  WHERE company_name IS NOT NULL
  -- this isn't valid; RETURNING can't reference users
  RETURNING companies.id, users.id AS user_id
) AS inserted_companies;

-- 4: drop users.company_name
ALTER TABLE users 
DROP COLUMN company_name;

COMMIT;

Similar questions that don't quite help:

CodePudding user response:

create table companies
(
    id           serial primary key ,
    company_name text
);
insert into companies (company_name)
select distinct company_name
from users
where company_name is not null;

alter table users add company_id int null;
update users set company_id = companies.id
from companies where companies.company_name = users.company_name;

ALTER TABLE users
DROP COLUMN company_name;

ALTER TABLE users
ADD CONSTRAINT users_company_id_fk foreign key (company_id) REFERENCES companies (id);

DBFiddle demo

CodePudding user response:

Building off @Cetin.Basoz's excellent answer, here's what I ended up with:

-- 1: add companies:
CREATE TABLE companies
(
    id           serial primary key,
    company_name text
);

-- 2: move users.company_name to companies.name
-- using the users.id as the companies.id for the initial import:
INSERT INTO companies (id, company_name)
SELECT id, company_name
FROM users
WHERE company_name IS NOT NULL;

-- 3: update the companies PK index
-- so we don't try to insert duplicate IDs:
SELECT setval('companies_id_seq', (SELECT MAX(id) FROM companies));

-- 4: add references to the newly-inserted companies:
ALTER TABLE users ADD company_id int null;
UPDATE users
SET company_id = users.id
WHERE company_name IS NOT NULL;

-- 5: drop the vestigial users.company_name
ALTER TABLE users DROP COLUMN company_name;

-- 6: add a FK index:
ALTER TABLE users
ADD CONSTRAINT users_company_id_fk
FOREIGN KEY (company_id)
REFERENCES companies (id);

The companies table will have holes in it, but as long as the id space is sufficiently large, I don't see that as a problem. It would have holes if records were deleted.

  • Related