Here 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 the constraint is not actually working:
SELECT id, user_id, class_type_id,deleted_at
FROM subscriptions;
Here is the output:
Why is uniqueness not being enforced?
Summary: in this tutorial, you will learn about PostgreSQL UNIQUE constraint to make sure that values stored in a column or a group of columns are unique across rows in a table. Sometimes, you want to ensure that values stored in a column or a group of columns are unique across the whole table such as email addresses or usernames.
So, the way Postgresql implements it, is very logical: first, you declare that a column should be unique, then, Postgresql adds the implementation of adding an unique index for you. "So I think it is what you call "partial uniqueness" by adding a constraint."
When you add a UNIQUE constraint to a column or a group of columns, PostgreSQL will automatically create a unique index on the column or the group of columns. The following statement creates a new table named person with a UNIQUE constraint for the email column.
Since various people have provided advantages of unique indexes over unique constraints, here's a drawback: a unique constraint can be deferred (only checked at the end of the transaction), a unique index can not be. How can this be, given that all unique constraints have a unique index?
Unique indexes in Postgres are based on values being equal, but NULL is never equal to anything, including other NULLs. Therefore any row with a NULL deleted_at value is distinct from any other possible row - so you can insert any number of them.
One way around this is to create partial indexes, applying different rules to rows with and without NULLs:
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 happens because of the NULL
value in the created_at
column. A unique index (or constraint) allows multiple rows with NULL
in them.
The only way you can prevent that is to either declare the column as NOT NULL
in order to force a value for it, or reduce the unique columns to (user_id, class_type_id)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With