PostgreSQL – Postgres unique constraint is not forced uniqueness

This is my constraint:

CREATE UNIQUE INDEX index_subscriptions_on_user_id_and_class_type_id_and_deleted_at
ON subscriptions
USING btree
(user_id, class_type_id, deleted_at);

This query proves that the constraint does not actually work:

SELECT id, user_id, class_type_id, deleted_at
FROM subscriptions;

This is the output:

Why is uniqueness not enforced?

The unique index in Postgres is based on equal values, but NULL is never equal to any value, including others NULL. Therefore, any row with NULL deleted_at value is different from any other possible row-so you can insert any number of rows.

One way to solve this problem is to create a partial index , Apply different rules to rows that contain and do not contain NULL:

CREATE UNIQUE INDEX ... ON subscriptions
(user_id, class_type_id) WHERE deleted_at IS NULL;

CREATE UNIQUE INDEX ... ON subscriptions
(user_id, class_type_id, deleted_at) WHERE deleted_at IS NOT NULL;

This is My constraints:

CREATE UNIQUE INDEX index_subscriptions_on_user_id_and_class_type_id_and_deleted_at
ON subscriptions
USING btree
(user_id, class_type_id, deleted_at);< /pre>

This query proves that the constraint does not actually work:

SELECT id, user_id, class_type_id,deleted_at
FROM subscriptions;

< p>This is the output:

Why is uniqueness not enforced?

The unique index in Postgres is based on the value equality, but NULL will never be equal to any value, including other NULLs. Therefore, any row with NULL deleted_at value is Unlike any other possible rows-so you can insert as many rows as you want.

One way to solve this problem is to create a partial index, applying different values ​​to rows that contain and do not contain NULL Rules:

CREATE UNIQUE INDEX ... ON subscriptions
(user_id, class_type_id) WHERE deleted_at IS NULL;

CREATE UNIQUE INDEX. .. ON subscriptions
(user_id, class_type_id, deleted_at) WHERE deleted_at IS NOT NULL;

Leave a Comment

Your email address will not be published.