Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Postgres - Unique constraint with multiple columns and NULL values

Tags:

postgresql

I have problem with Postgres Unique constraint with multiple columns that may contain NULL value.

Let's assume this situation:

CREATE TEMP TABLE test (
  foo TEXT,
  bar TEXT,
  UNIQUE (foo, bar)
);

INSERT INTO test
VALUES 
  ('foo', NULL),
  ('foo', NULL),
  ('foo', 'bar'),
  ('foo', 'bar')
ON CONFLICT (foo, bar) DO NOTHING;

Insert will insert ('foo', 'bar') once and ('foo', NULL) twice (even though the intuition says it should insert once).

In this scenario solution is pretty straightforward. I could just add unique index

CREATE UNIQUE INDEX indx ON test (foo) WHERE bar IS NULL;

But the problem starts when there is more columns and with different types (not only text). Let's say we have 10 columns and 9 of them can have NULL value. Maybe I could solve it with big amount of constraints, but it's not convenient at all.

Is there easier way to keep uniqueness for a row like that?

like image 440
puchal Avatar asked Feb 13 '20 11:02

puchal


People also ask

What is a unique constraint in PostgreSQL?

PostgreSQL unique constraint. null: Allowing only one Null. While the SQL standard allows multiple nulls in a unique column, and that is how Postgres behaves, some database systems (e.g. MS SQL) allow only a single null in such cases.

Does Postgres allow multiple Nulls in a column?

While the SQL standard allows multiple nulls in a unique column, and that is how Postgres behaves, some database systems (e.g. MS SQL) allow only a single null in such cases. Users migrating from other database systems sometimes want to emulate this behavior in Postgres.

Why can nullable columns with unique constraints contain multiple null values?

There is a long discussion on why nullable columns with a UNIQUE constraint can contain multiple NULL values. The short version is that NULL represents missing information and comparing a field with missing information with another makes no sense.

How do I create a unique index in PostgreSQL?

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.


2 Answers

An alternative to the good solution of forbidding NULLs is to create a unique index.

All you need is a value that is guaranteed not to occur in your data set (in my example '@@'):

CREATE UNIQUE INDEX ON test (
   coalesce(foo, '@@'),
   coalesce(bar, '@@')
);
like image 188
Laurenz Albe Avatar answered Oct 25 '22 20:10

Laurenz Albe


You might consider the column definition in general, as both are stated as texts, you can just let them be NOT NULLABLE and provide a DEFAULT value as ' ' (empty string). This way you can be sure, that foo does not get saved twice. Also NULL values are not that good in practice, because its just a sign that there is a MISSING value.

CREATE TEMP TABLE test (
  foo TEXT DEFAULT '' NOT NULL,
  bar TEXT DEFAULT '' NOT NULL,
  UNIQUE (foo, bar)
);
like image 35
phaen Avatar answered Oct 25 '22 20:10

phaen