views:

55

answers:

4

I'm posting this question which is somewhat a summary of my other question.

I have two databases:
1) db_users.
2) db_friends.

I stress that they're stored in separate databases on different servers and therefore no foreign keys can be used.

In 'db_friends' I have the table 'tbl_friends' which has the following columns:
- id_user
- id_friend

Now how do I make sure that each pair is unique at this table ('tbl_friends')?
I'd like to enfore that at the table level, and not through a query.

For example these are invalid rows:
1 - 2
2 - 1

I'd like this to be impossible to add.

Additionally - how would I seach for all of the friends of user 713 while he could be mentioned, on some friendship rows, at the second column ('id_friend')?

+1  A: 

Well, a unique constraint on the pair of columns will get you half way there. I think the easiest way to ensure you don't get the reversed version would be to add a constraint ensuring that id_user < id_friend. You will need to compensate for this ordering at insertion time, but it will get you the database Level constraint you desire without duplicating data or relying on foreign keys.

As for the second question, to find all friends for id=1 you could select id_user, id_friend from tbl_friend where id_user = 1 or id_friend = 1 and then in your client code throw out all the 1's regardless of column.

easel
+2  A: 

You're probably not going to be able to do this at the database level -- your application code is going to have to do this. If you make sure that your tbl_friends records always go in with (lowId, highId), then a typical PK/Unique Index will solve the duplicate problem. In fact, I'd go so far to rename the columns in your tbl_friends to (id_low, id_high) just to reinforce this.

Your query to find anything with user 713 would then be something like

SELECT id_low AS friend FROM tbl_friends WHERE (id_high = ?)
UNION ALL
SELECT id_high AS friend FROM tbl_friends WHERE (id_low = ?)

For efficiency, you'd probably want to index it forward and backward -- that is by (id_user, id_friend) and (id_friend, id_user).

If you must do this at a DB level, then a stored procedure to swap arguments to (low,high) before inserting would work.

Rick O
Thanks! ........
Poni
+3  A: 

You'd have to use a trigger to enforce that business rule.
Making the two columns in tbl_friends the primary key (unique constraint failing that) would only ensure there can't be duplicates of the same set: 1, 2 can only appear once but 2, 1 would be valid.

how would I seach for all of the friends of user 713 while he could be mentioned, on some friendship rows, at the second column ('id_friend')?

You could use an IN:

WHERE 713 IN (id_user, id_friend)

..or a UNION:

JOIN (SELECT id_user AS user
        FROM TBL_FRIENDS
      UNION ALL
      SELECT id_friend
        FROM TBL_FRIENDS) x ON x.user = u.user
OMG Ponies
Thanks for the reply! Which one of the above queries is better in performance terms?
Poni
@Poni: I always recommend checking the EXPLAIN/query plan for your own system to see which performs best.
OMG Ponies
A: 

One way you could do it is to store the two friends on two rows:

CREATE TABLE FriendPairs (
  pair_id INT NOT NULL,
  friend_id INT NOT NULL,
  PRIMARY KEY (pair_id, friend_id)
);

INSERT INTO FriendPairs (pair_id, friend_id)
VALUES (1234, 317), (1234, 713);

See? It doesn't matter which order you insert them, because both friends go in the friend_id column. So you can enforce uniqueness easily.

You can also query easily for friends of 713:

SELECT f2.friend_id
FROM FriendPairs AS f1
JOIN FriendPairs AS f2 ON (f1.pair_id = f2.pair_id)
WHERE f1.friend_id = 713
Bill Karwin
Thank you for taking the time to answer Bill - well, that seems to involve way too much overhead - both from the amount of rows aspect (double size) and from the query aspect where I need to JOIN and stuff.. I'd better put a constraint (id_user < id_friend) when inserting just like Rick and easel said, don't you think?
Poni
@Poni: No, that wouldn't help. If you have pairs like (137,317) and (317,713), then user 317 can still end up in either the first or second column even if you have enforced `id_user<id_friend`. So to search for user 317, you still need to use complex queries like @OMG Ponies and @Rick O demonstrated.
Bill Karwin