PostgreSQL - Unique Constraint vs Unique Index
Introduction
When designing a database, ensuring data integrity is crucial. One common requirement is to make sure that certain columns in your tables do not contain duplicate values. PostgreSQL offers two mechanisms to enforce this: the UNIQUE CONSTRAINT
and the UNIQUE INDEX
. While they might seem similar, understanding their differences can help you make better decisions for your database design.
In this post, we’ll investigate how each of these works, and when you might prefer one over the other.
Goal
The goal is straightforward: we want a column in a table to contain only unique values, meaning that any attempt to insert a duplicate value should result in an error.
Investigation
To begin, let’s create a simple users
table to demonstrate the use of a UNIQUE CONSTRAINT
.
CREATE TABLE users (
id integer PRIMARY KEY,
email text,
UNIQUE (email)
);
Testing the UNIQUE CONSTRAINT
We’ll add a row and then attempt to insert a duplicate email.
INSERT INTO users VALUES (1, '[email protected]');
INSERT INTO users VALUES (2, '[email protected]');
As expected, the database throws an error, preventing the duplicate entry.
ERROR: duplicate key value violates unique constraint “users_email_key”
This shows that the UNIQUE CONSTRAINT
successfully enforces uniqueness.
Creating the Unique Index
Next, let’s explore how a UNIQUE INDEX
operates by creating another table.
CREATE TABLE users1 (
id INTEGER PRIMARY KEY,
email TEXT
);
CREATE UNIQUE INDEX email_idx ON users1 (email);
Testing the UNIQUE INDEX
We perform the same insert operations:
INSERT INTO users1 VALUES (1, '[email protected]');
INSERT INTO users1 VALUES (2, '[email protected]');
Again, the database prevents the duplicate entry, throwing an error similar to the one we saw earlier.
ERROR: duplicate key value violates unique constraint “email_idx”
This confirms that a UNIQUE INDEX
can also enforce uniqueness.
Outcome
At this point, it’s clear that both UNIQUE CONSTRAINT
and UNIQUE INDEX
can achieve the same goal of preventing duplicate values.
But the question remains: which one to use ?
Research
To gain more insight, let’s see what others in the community are saying about these concepts.
Community Insights
On Reddit, user cldellow points out a subtle advantage: “Constraints can be deferred,” linking to the PostgreSQL documentation on the topic. This feature allows for more flexibility in certain operations.
Another user, bendem, adds, “Constraints are modeling concepts, indexes are optimizations,” highlighting a key distinction in their intended use.
StackOverflow
A StackOverflow thread echoes similar sentiments, with user David Spillett noting that “Under the hood, a unique constraint is implemented the same way as a unique index.” Although, the comment is about SQL Server (and not about Postgres), it’s still an useful information.
PostgreSQL Documentation
The PostgreSQL documentation clarifies that adding a UNIQUE CONSTRAINT
will automatically create a unique B-tree index on the specified columns. The documentation also states that a UNIQUE INDEX
is the mechanism that enforces the constraint.
“Adding a unique constraint will automatically create a unique B-tree index on the column or group of columns listed in the constraint.” — Documentation: 16: 5.4. Constraints1
“PostgreSQL automatically creates a unique index when a unique constraint or primary key is defined for a table. The index … is the mechanism that enforces the constraint.” — Documentation: 16: 11.6. Unique Indexes2
Conclusion
In summary, both UNIQUE CONSTRAINT
and UNIQUE INDEX
enforce uniqueness in PostgreSQL, but they serve slightly different purposes.
Here’s what we’ve learnt:
- Both
UNIQUE CONSTRAINT
&UNIQUE INDEX
can be used to enforce uniqueness. - A
UNIQUE CONSTRAINT
is a rule. Under the hood it is implemented or enforced using aUNIQUE INDEX
. - A
UNIQUE INDEX
is automatically created when we define aUNIQUE CONSTRAINT
. - If the goal is to enforce uniqueness, use a
UNIQUE CONSTRAINT
. This will give a hint to future maintainers about its purpose. - If the goal is to search/sort the rows or you have a complex requirement to enforce uniqueness conditionally (e.g. Partial Indexes), use a
UNIQUE INDEX
. - Do not create both of them on the same column(s).
Understanding these nuances helps in designing a robust and efficient database that meets your specific needs.