PostgreSQL Unique Index
Ensure data uniqueness with unique indexes
🔒 What is a Unique Index?
A unique index ensures no two rows have the same value in indexed columns. It speeds up queries while enforcing data integrity by preventing duplicate entries automatically.
-- Create a unique index
CREATE UNIQUE INDEX idx_unique_email
ON users(email);
Output:
CREATE INDEX
Key Unique Index Features
Uniqueness
Prevents duplicate values
-- No duplicate emails allowed
CREATE UNIQUE INDEX idx_email
ON users(email);
Integrity
Maintains data quality
-- Unique usernames
CREATE UNIQUE INDEX idx_username
ON accounts(username);
Performance
Fast lookups + uniqueness
-- Speed + uniqueness
CREATE UNIQUE INDEX idx_sku
ON products(sku);
Multi-Column
Unique combinations
-- Unique user-product pair
CREATE UNIQUE INDEX idx_user_product
ON favorites(user_id, product_id);
🔹 Creating a Unique Index
Unique indexes prevent duplicate values in a column. They're perfect for email addresses, usernames, or any field that must be unique across all rows.
-- Create a users table
CREATE TABLE users (
id SERIAL PRIMARY KEY,
email VARCHAR(255),
username VARCHAR(50)
);
-- Create unique index on email
CREATE UNIQUE INDEX idx_unique_email ON users(email);
-- This insert works
INSERT INTO users (email, username) VALUES ('[email protected]', 'john123');
-- This fails - duplicate email!
INSERT INTO users (email, username) VALUES ('[email protected]', 'john456');
Output:
INSERT 0 1
ERROR: duplicate key value violates unique constraint
🔹 Unique Index vs UNIQUE Constraint
Both enforce uniqueness, but constraints are more explicit and show up in table definitions. Unique indexes offer more flexibility with partial and expression indexes.
-- Method 1: UNIQUE constraint (recommended for simple cases)
CREATE TABLE products (
id SERIAL PRIMARY KEY,
sku VARCHAR(50) UNIQUE,
name VARCHAR(200)
);
-- Method 2: Unique index (more flexible)
CREATE TABLE products (
id SERIAL PRIMARY KEY,
sku VARCHAR(50),
name VARCHAR(200)
);
CREATE UNIQUE INDEX idx_unique_sku ON products(sku);
Output:
Both methods prevent duplicate SKUs effectively
🔹 Multi-Column Unique Index
Create unique indexes on multiple columns to ensure combinations are unique. This is useful for preventing duplicate relationships like a user liking the same post twice.
-- Create a favorites table
CREATE TABLE favorites (
id SERIAL PRIMARY KEY,
user_id INTEGER,
product_id INTEGER
);
-- Ensure each user can favorite a product only once
CREATE UNIQUE INDEX idx_unique_user_product
ON favorites(user_id, product_id);
-- First favorite works
INSERT INTO favorites (user_id, product_id) VALUES (1, 100);
-- Second favorite for same combination fails
INSERT INTO favorites (user_id, product_id) VALUES (1, 100);
Output:
INSERT 0 1
ERROR: duplicate key value
🔹 Unique Index with NULL Values
By default, unique indexes allow multiple NULL values because NULL is not equal to NULL. Use NULLS NOT DISTINCT for stricter uniqueness including NULLs.
-- Standard unique index (multiple NULLs allowed)
CREATE UNIQUE INDEX idx_phone ON users(phone);
-- These both work - NULL is not equal to NULL
INSERT INTO users (email, phone) VALUES ('[email protected]', NULL);
INSERT INTO users (email, phone) VALUES ('[email protected]', NULL);
-- PostgreSQL 15+: Only one NULL allowed
CREATE UNIQUE INDEX idx_phone_strict
ON users(phone) NULLS NOT DISTINCT;
Output:
Standard index: Multiple NULLs allowed
NULLS NOT DISTINCT: Only one NULL allowed
🔹 Case-Insensitive Unique Index
Make unique indexes case-insensitive using LOWER() function. This prevents users from creating accounts with 'John' and 'john' as separate usernames.
-- Create case-insensitive unique index
CREATE UNIQUE INDEX idx_username_lower
ON users(LOWER(username));
-- This insert works
INSERT INTO users (username, email)
VALUES ('JohnDoe', '[email protected]');
-- This fails - 'johndoe' matches 'JohnDoe' when lowercased
INSERT INTO users (username, email)
VALUES ('johndoe', '[email protected]');
Output:
INSERT 0 1
ERROR: duplicate key value
🔹 Best Practices
Follow these guidelines to use unique indexes effectively and maintain data integrity while keeping your database performant and reliable.
✅ Do:
- Use unique indexes for email addresses and usernames
- Create unique indexes on natural keys (SKU, ISBN, etc.)
- Use multi-column unique indexes for composite keys
- Consider case-insensitive indexes for user input
❌ Don't:
- Create unique indexes on columns with many duplicates
- Forget to handle unique constraint errors in your application
- Use unique indexes when you need soft deletes (use partial indexes)