Search code examples
sqlpostgresql

Create a new row for every missing reference in a table


In short, I am trying to do this:

Setup:

create table info(
  info_id serial primary key, 
  info_text text
);
create table product(
  prod_id serial primary key, 
  info_id int /*unique*/, 
  description text unique, 
  price int);

insert into product (description, price) values ('pen', 1), ('notebook', 2), ('ruller', 3);

Now I am trying to update all product rows where the info_id reference is null, by way of creating a new row in the info table and then assign the reference to it to the product:

with new_info as (insert into info default values returning info_id)
update product
set info_id = (select info_id from new_info)
where product.info_id is null;

The problem is that it only creates one info and then assigns all products to it. I expect it to create one companion info row for each product.

select * from product, info where product.info_id = info.info_id;
 prod_id | info_id | description | price | info_id | info_text 
---------+---------+-------------+-------+---------+-----------
       1 |       1 | pen         |     1 |       1 | [null]
       2 |       1 | notebook    |     2 |       1 | [null]
       3 |       1 | ruller      |     3 |       1 | [null]

Solution

  • This creates a separate new row in table info and backfills a reference to it for every null in product.info_id:

    WITH upd AS (
       UPDATE product
       SET    info_id = nextval(pg_get_serial_sequence('info', 'info_id'))
       WHERE  info_id IS NULL
       RETURNING info_id
       )
    INSERT INTO info (info_id)
    SELECT info_id
    FROM   upd;
    

    fiddle

    Even works with a FK constraint in place.

    Related: