Is it possible in PostgreSQL to create a deferrable unique constraint on a character column, but case-insensitive?
Let's assume the following basic table:
CREATE TABLE sample_table (
my_column VARCHAR(100)
);
If deferrable constraint is not needed, it is as simple as creating unique index with function, e.g.:
CREATE UNIQUE INDEX my_unique_index ON sample_table(UPPER(my_column));
Deferred constraint check requires creating the constraint explicitly, e.g.:
ALTER TABLE sample_table
ADD CONSTRAINT my_unique_constraint UNIQUE(my_column)
DEFERRABLE INITIALLY IMMEDIATE;
And unfortunately it is not possible to use arbitrary functions in unique constraint.
One possible workaround would be to create additional column with the same content as my_column
, but upper case, updated via a trigger after each update/insert, then create a deferrable unique constraint on this artificial column. This, however, sounds like a really ugly hack.
Alternatively, it should be possible to use CREATE CONSTRAINT TRIGGER
and manually check for case-insensitive uniqueness (of course a regular index would still be necessary). This sounds a bit overcomplicated for such a simple (and popular, I suppose) requirement.
Is there any simpler and/or more elegant way around this limitation?
But there's a problem, the UNIQUE constraint is case sensitive! But don't worry there's a workaround! Instead of using the text data type, we can use the citext (case insensitive text) type!
What is a unique constraint in PostgreSQL? A unique constraint is a single field or combination of fields that uniquely defines a record. Some of the fields can contain null values as long as the combination of values is unique.
The citext data type allows you to eliminate calls to lower in SQL queries, and allows a primary key to be case-insensitive. citext is locale-aware, just like text , which means that the matching of upper case and lower case characters is dependent on the rules of the database's LC_CTYPE setting.
You can circumvent the restriction by using the special type citext
provided by the additional module of the same name. Quoting the manual:
The
citext
module provides a case-insensitive character string type, citext. Essentially, it internally calls lower when comparing values. Otherwise, it behaves almost exactly liketext
.
It addresses your case exactly. Run once per database:
CREATE EXTENSION citext;
Then you can:
CREATE TABLE sample_table (
my_column citext
,CONSTRAINT my_unique_constraint UNIQUE(my_column)
DEFERRABLE INITIALLY IMMEDIATE
);
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