Why & when you should use PostgreSQL deferred uniqueness constraints

Article autor
September 9, 2025
Why & when you should use PostgreSQL deferred uniqueness constraints
Elixir Newsletter
Join Elixir newsletter

Subscribe to receive Elixir news to your inbox every two weeks.

Oops! Something went wrong while submitting the form.
Elixir Newsletter
Expand your skills

Download free e-books, watch expert tech talks, and explore open-source projects. Everything you need to grow as a developer - completely free.

Table of contents

Learn a trick that will allow you to manage item order in Postgres tables easier & faster.

When you want to ensure that a certain column in a table has unique values, what you usually do is create a UNIQUE constraint for the column (or e.g. for a pair of columns, when you want the uniqueness to apply to pairs of values in each tuple).

postgres=# CREATE TABLE numbers (number INTEGER, UNIQUE (number));
CREATE TABLE

postgres=# INSERT INTO numbers VALUES (0), (1), (2);
INSERT 0 3

Suppose that the number column represents an ordering, and you want all (or a subset of) tuples' index to be shifted by 1 to make way for a new item at a given index. Your first guess is probably this:

postgres=# UPDATE numbers SET number = number + 1;
ERROR:  duplicate key value violates unique constraint "numbers_number_key"
DETAIL:  Key (number)=(1) already exists.

Why did it fail? Because even though conceptually it would lead to the column's values of (1), (2), (3) - which would not violate the uniquenes constraing - the uniqueness constraint is checked immediately after updating each row. So, transitively, the column is briefly in a state of (1), (1), (2), which creates the error.

To make it work, we have to instruct the database to enforce this constraint at transaction commit time. Here's how to create the table with such setting default for this constraint:

postgres=# CREATE TABLE numbers (number INTEGER, UNIQUE (number) DEFERRABLE INITIALLY DEFERRED);
CREATE TABLE

postgres=# INSERT INTO numbers VALUES (0), (1), (2);
INSERT 0 3

postgres=# UPDATE numbers SET number = number + 1;
UPDATE 3

Clearly, it's working now. Read more on how to control the constraint's enforcing strategy on a per-transaction basis.

Work with a team that keeps learning and building better software every day.

Related posts

Dive deeper into this topic with these related posts

No items found.

You might also like

Discover more content from this category

Style spacing between repeated elements in CSS using flex gap

It's a pretty common scenario - you have to place a few elements in equal distances. E.g. unordered list items.

Treating warnings as errors in Elixir's mix compile

Warnings in Elixir are usually an important sign of a problem in the codebase. There is an easy way to make them gone.

How to process Phoenix conn after render before it is sent as a response

There are a bunch of operations you may want to perform before the rendered response in conn is sent to the client, such as minification. In this post I'll show you how to do it easily.