@Catcall's CHECK
constraint enforces that exactly one of (a, b)
must be NULL
.
But NULL
values do not violate a UNIQUE constraint - two NULL are never considered equal! Therefore, the simple UNIQUE
constraint doesn't work.
You can get it done with two partial UNIQUE indices:
CREATE TEMP TABLE foo (
a int
,b int
,c int NOT NULL
,d int NOT NULL
,CHECK ((a IS NOT NULL AND b IS NULL) OR (b IS NOT NULL AND a IS NULL))
);
CREATE UNIQUE INDEX foo_acd_idx ON foo(a,c,d)
WHERE b is NULL;
CREATE UNIQUE INDEX foo_bcd_idx ON foo(b,c,d)
WHERE a is NULL;
INSERT INTO foo VALUES (NULL,2,3,4);
INSERT INTO foo VALUES (NULL,2,3,4); -- error!
I declared c
and d
as NOT NULL
, to prevent further complications.
More details in this closely related answer.
If you also want to disallow (1, NULL, 3, 4)
and (NULL, 1, 3, 4)
(see my comment), you could use one index with COALESCE
instead:
CREATE UNIQUE INDEX foo_xcd_idx ON foo(COALESCE(a,b),c,d);