Database Administrators Stack Exchange is a question and answer site for database professionals who wish to improve their database skills and learn from others in the community. Join them; it only takes a minute:

Sign up
Here's how it works:
  1. Anybody can ask a question
  2. Anybody can answer
  3. The best answers are voted up and rise to the top

I have two tables: a user table and a friendship table. Say the friendship table looks like this:

friendship
----------
user_one_id
user_two_id
other_fields

I need to enforce the uniqueness of the pair (user_one_id, user_two_id) and disregard the ordering. So:

user_one_id | user_two_id
-------------------------
      1     |      2
      2     |      1  // database should throw a unique constraint error here

Is there a way to do this using constraints or should I implement this some other way?

EDIT: another important point is that user_one_id represents the initiator of the friendship and user_two_id represents the recipient, so I cannot just make the "smaller" of the two ids user_one_id.

share|improve this question

In light of your comment,

For my use case, user_one_id represents the initiator of the friendship, and user_two_id represents the recipient of the friendship. So I can't just use the lowest value as user_one_id.*

Well, you can still do it. Your use case just excludes a row-constraint to ensure that. What you want is to use a table-constraint, something like this.

CREATE TABLE friendship (
  user_one_id int NOT NULL,
  user_two_id int NOT NULL,
  CHECK (user_one_id != user_two_id ),
  PRIMARY KEY (user_one_id, user_two_id)
);
-- you can do least first if you want. doesn't matter.
CREATE UNIQUE INDEX ON friendship (
  greatest(user_one_id, user_two_id),
  least(user_one_id, user_two_id)
);

We have a lot going on here.. We make sure.

  1. Both are NOT NULL
  2. Both are not equal to each other
  3. Both are UNIQUE (user_one_id, user_two_id)

That leaves one remaining problem of commutative uniqueness we solve that with a custom unique table-constraint implemented with an index.

Proof in the pudding

INSERT INTO friendship VALUES ( 1,2 );
INSERT 0 1
test=# INSERT INTO friendship VALUES ( 2,1 );
ERROR:  duplicate key value violates unique constraint friendship_greatest_least_idx"
DETAIL:  Key ((GREATEST(user_one_id, user_two_id)), (LEAST(user_one_id, user_two_id)))=(2, 1) already exists.

As an important friendly note your names are all kinds of silly. The relationship is fine. In production, please give them better names..

friendship
----------
request_initiator
request_target
other_fields
share|improve this answer
    
Correct solution, there is a small detail, though, that could be improved: your solution creates two indexes in the background, where one would suffice. Create the table without primary key, then an index over (GREATEST(),LEAST()) and do ALTER TABLE ADD PRIMARY KEY USING INDEX... – Twinkles 2 hours ago
    
Although, now that I think about it, most real world databases will probably benefit from the second index anyway. – Twinkles 2 hours ago

If you set the logic to set that the value of user_one_id is lower than the value of user_two_id, you won't have any issue.

share|improve this answer
1  
For my use case, user_one_id represents the initiator of the friendship, and user_two_id represents the recipient of the friendship. So I can't just use the lowest value as user_one_id. – maniciam 5 hours ago
    
@maniciam see my answer. – Evan Carroll 5 hours ago

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.