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:
2
Answers
Building off @Cetin.Basoz's excellent answer, here's what I ended up with:
The
companies
table will have holes in it, but as long as theid
space is sufficiently large, I don't see that as a problem. It would have holes if records were deleted.DBFiddle demo