Home > Net >  Update elements because foreign key column has changed
Update elements because foreign key column has changed

Time:08-27

I have the following (simplified) structure of a device and log messages which are attached to a device:

CREATE TABLE device (
    id SERIAL PRIMARY KEY,
    name VARCHAR(100),
    serial_number VARCHAR(20) UNIQUE
);

CREATE TABLE error_log (
    id SERIAL PRIMARY KEY,
    device_id INT NOT NULL REFERENCES device(id),
);   

I know want to change the schema to use the serial_number column of the device as the primary key, and drop the automatic key id. As part of the process I need to add a new column device_serial_number to the error_log table. My question is how to assign the values to this column using only SQL.

If a had an ORM mapper in action my pseudo code would look like this:

for log in error_log.all_objects():
    log.device_serial_number = log.device.serial_number

Can I do this in pure SQL? (Postgresql if that matters)

CodePudding user response:

You need to update the rows in the error_log table where the device.id = error.log match to device.serial_number.

So, first add a column to error_log. And then execute an UPDATE statement as following:

update error_log
  set serial_number = d.serial_number
from device d 
where error_log.id = d.id;

Afterwards you should add a UNIQUE constraint to the error_log.serial_number and then you can remove the error_log.device_id column if you don't need it anymore.

CodePudding user response:

This is doable, just not in a single step.

add the new column to the error_log table. This must be nullable for now!

alter table error_log 
  add device_serial_number varchar(20);

Populate the new column with the correct values

update error_log
  set device_serial_number = d.serial_number
from device d
where error_log.device_id = d.id;

Now the new column can be set to NOT NULL and we can remove the existing foreign key.

alter table error_log
  alter column device_serial_number set not null,
  drop constraint error_log_device_id_fkey;

Now drop the ID column, drop the old unique constraint and define a new primary key. Dropping the old unique constraint will remove the underlying unique index which is no longer needed as the primary key will create a new one.

alter table device
   drop column id,
   drop constraint device_serial_number_key,
   add primary key (serial_number);

-- re-create the foreign key to the new primary key    
alter table error_log
   drop column device_id,
   add foreign key (device_serial_number) 
     references device(serial_number);   

Note that the drop constraint error_log_device_id_fkey and drop constraint device_serial_number_key assume the default naming convention that Postgres applies for "unnamed" constraints. You need to check if that really is the name of your foreign key.

  • Related