DEV Community

Cover image for How simultaneously swapping unique IDs in Postgres
Hamid Shoja
Hamid Shoja

Posted on Edited on Originally published at substack.com

How simultaneously swapping unique IDs in Postgres

Hi friends

Here's a quick tip for when you're working with Postgres, this came in clutch for me when I had to use DEFERRABLE INITIALLY IMMEDIATE

I'm sharing this with a practical example where two rows in a table must exchange unique values simultaneously within the same transaction. This is common when swapping unique IDs or handling cyclic dependencies.

Example: Swapping Unique Teacher IDs

Consider a classes table where each class has a unique teacher_id, but you need to swap the teachers between two classes. The UNIQUE constraint would normally prevent this in a single UPDATE statement because, mid-operation, there would temporarily be duplicate teacher_id values.

So we can Define the table with a unique constraint that is DEFERRABLE INITIALLY IMMEDIATE:

CREATE TABLE classes (
    id INT PRIMARY KEY,
    teacher_id INT NOT NULL UNIQUE DEFERRABLE INITIALLY IMMEDIATE,
    class_name VARCHAR(100)
);
-- Insert initial data
INSERT INTO classes VALUES (1, 101, 'Biology'), (2, 102, 'Chemistry');
Enter fullscreen mode Exit fullscreen mode

By default, the database checks the uniqueness after every statement.
Perform the swap within a transaction by explicitly deferring the constraint check:

BEGIN;
-- Temporarily set the specific constraint to DEFERRED mode for this transaction
SET CONSTRAINTS classes_teacher_id_key DEFERRED;

-- The update statements can now run without immediate failure
UPDATE classes SET teacher_id = 102 WHERE id = 1; -- Temporarily creates duplicate 102
UPDATE classes SET teacher_id = 101 WHERE id = 2; -- Resolves the duplicate

COMMIT;
-- At the COMMIT point, the database checks all deferred constraints.
-- Since the final state is valid (101 and 102 are unique again), the transaction succeeds.
Enter fullscreen mode Exit fullscreen mode

If you tried these updates without the BEGIN; ... SET CONSTRAINTS DEFERRED; ... COMMIT; block, the first UPDATE statement would immediately fail due to the INITIALLY IMMEDIATE setting, which acts as a per-statement check.

this example demonstrate how to use DEFERRABLE INITIALLY IMMEDIATE constraints to swap unique values within a transaction:

Hope that helped!

Hash

Top comments (0)