4

My postgres database has a column called "id" that runs from 40,000,000 to about 50,000,000. The "id" column is the primary key. I need to change the "id" column values such that they span different numbers in order to merge this database with another.

How can I go about generating code to change the values from 40,000,000 - 50,000,000 to, say, 0 - 10,000,000?

The table definition is

CREATE TABLE public.keyvaluehistory (
 id bigint NOT NULL
 DEFAULT nextval('keyvaluehistory_id_seq'::regclass),
 segkey text NOT NULL,
 dvalue double precision,
 bvalue bytea,
 tstamp timestamp with time zone,
 CONSTRAINT keyvaluehistory_pkey PRIMARY KEY (id)
);

There are no foreign keys on the table.

I can afford downtime on the order of minutes/hours.

Laurenz Albe
62.1k4 gold badges57 silver badges93 bronze badges
asked Oct 7, 2019 at 0:28
1
  • 2
    Subtract 40,000,000? Commented Oct 7, 2019 at 1:16

1 Answer 1

5

I would add another column temporarily.

The first part can run while the database is active:

ALTER TABLE keyvaluehistory ADD new_id bigint;
CREATE SEQUENCE keyvaluehistory_new_id_seq OWNED BY keyvaluehistory.new_id;
/* update in batches to avoid table bloat */
UPDATE keyvaluehistory SET new_id = id - 39999999
 WHERE id BETWEEN 40000000 AND 40999999;
VACUUM keyvaluehistory;
UPDATE keyvaluehistory SET new_id = id - 39999999
 WHERE id BETWEEN 41000000 AND 41999999;
VACUUM keyvaluehistory;
...
SET maintenance_work_mem = '1GB';
CREATE UNIQUE INDEX CONCURRENTLY keyvaluehistory_new_pkey (new_id);

The following part locks the table and requires down time.

The most time consuming part is adding the primary key, because that requires scanning the table.

/* downtime starts here */
BEGIN;
LOCK TABLE keyvaluehistory IN ACCESS EXCLUSIVE MODE;
/* catch up */
UPDATE keyvaluehistory SET new_id = id - 39999999
 WHERE new_id IS NULL;
ALTER TABLE keyvaluehistory
 DROP CONSTRAINT keyvaluehistory_pkey;
ALTER TABLE keyvaluehistory
 DROP COLUMN id;
ALTER TABLE keyvaluehistory
 ADD CONSTRAINT keyvaluehistory_pkey USING keyvaluehistory_new_pkey;
ALTER INDEX keyvaluehistory_new_pkey RENAME TO keyvaluehistory_pkey;
ALTER TABLE keyvaluehistory RENAME new_id TO id;
ALTER SEQUENCE keyvaluehistory_new_id_seq RENAME TO keyvaluehistory_id_seq;
SELECT setval('keyvaluehistory_new_id_seq', 10000001);
COMMIT;

Please test befor running it in production; I may have forgotten something.

answered Oct 8, 2019 at 2:31
3
  • This worked! There was some language/variable mismatch with the indexes and constraints but I managed to work it out. Thanks a lot! Commented Oct 8, 2019 at 22:19
  • @Laurenz: can you explain why VACUUM after each block avoids table-bloat? Commented May 30, 2021 at 12:44
  • 1
    @TmTron Because it removes the dead row versions created in the preceding step, so that the space can be reused by the next UPDATE. See the PostgreSQL documentation. Commented May 31, 2021 at 1:39

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.