2

Suppose, I have the following table:

CREATE TABLE "user" (
    id     BIGINT PRIMARY KEY NOT NULL,
    phone1 VARCHAR,
    phone2 VARCHAR
);

And I need to implement the following limitation: all phone numbers (if any) in table must be unique.

i.e database should not allow any of the following situations:

id | phone1 | phone2
1  | 111    | 111

id | phone1 | phone2
1  | 111    | NULL
2  | 111    | NULL  

id | phone1 | phone2
1  | 111    | NULL
2  | NULL   | 111 

I know how to implement constraints for first two examples, but I'm stuck with the third one. Is there any way to do this?

Vao Tsun
  • 47,234
  • 13
  • 100
  • 132
Marat Safin
  • 1,793
  • 20
  • 28

2 Answers2

1

You cannot easily do this. The least()/greatest() approach will not work in all cases.

Postgres does have some fancy index operations. But the best way is to use a junction table. For instance:

create table userPhones (
    userPhoneId bigint primary key ,
    userId bigint references users(id),
    phone_counter int check (phone_counter in (1, 2)),
    phone varchar,
    unique (userId, phone_counter),
    unique(phone)
);

This also limits the number of phone numbers to 2 for each user.

Gordon Linoff
  • 1,242,037
  • 58
  • 646
  • 786
0

try an old trick:

db=# create unique index on "user" (least(phone1,phone2), greatest(phone1,phone2));
CREATE INDEX
Time: 14.507 ms
db=# insert into "user" values(1,111,111);
INSERT 0 1
Time: 35.017 ms

rest will fail:

db=# insert into "user" values(2,111,null);
ERROR:  duplicate key value violates unique constraint "user_least_greatest_idx"
DETAIL:  Key ((LEAST(phone1, phone2)), (GREATEST(phone1, phone2)))=(111, 111) already exists.
Time: 10.323 ms
db=# insert into "user" values(2,null,111);
ERROR:  duplicate key value violates unique constraint "user_least_greatest_idx"
DETAIL:  Key ((LEAST(phone1, phone2)), (GREATEST(phone1, phone2)))=(111, 111) already exists.
Time: 5.553 ms
db=# insert into "user" values(1,111,111);
ERROR:  duplicate key value violates unique constraint "user_pkey"
DETAIL:  Key (id)=(1) already exists.
Time: 11.067 ms
Vao Tsun
  • 47,234
  • 13
  • 100
  • 132
  • 1
    Thanks. But this is not enough for that case: insert into "test" values(2,NULL,222); insert into "test" values(3,222,111); So I added also create unique index on "test" (least(phone1,phone2)); create unique index on "test" (greatest(phone1,phone2)); and it worked – Marat Safin Aug 01 '18 at 09:04