views:

427

answers:

2

Hello.

I have two tables.

USER user_id password

FRIEND_LIST user_id friend_id

If user 1 is friend of user 2 then in friend_list there will be 2 records:

1 2
2 1

Thats how I'm controlling friend list.

My question is how can I create an efficient query that validates if a user is a friend of a friend.

For example user 1 has in his friend list user 2. and user 3 has in his friend list user 2. So user 2 is a common friend of both 1 and 3.

Here is how friend_list table looks like:

1 2
2 1
3 2
2 3

No I want to know if user 1 has a friend that has as friend user 3.

The pseudocode is as follows:

validate(){    
valid = false
    list = get all friends from user 1 and store them in 'list'.
    for each friend in list {
       list2 = get all friends from friend
       for each friend2 in list2 {
        if friend2.user_id = 3 }
          valid = true
          break; //stop here because we have found that 3 is a friend of a friend of 1
        }
     }
    }
return valid
}

This is how it would look like in a programming language. Now I want to validate the same but just with an SQL query.

I tried this but I dont know if this is a good way to validate that.

select *
from friend_list fl1
inner join friend_list fl2 on fl1.user_id = fl2.user_id
inner join friend_list fl3 on fl2.friend_id = fl3.user_id
where fl1.user_id = 1 and fl3.friend_id = 3

Thanks in advance.

Thank you very much for your support. This is the first time I use this forum and helped me a lot.

I used the EXISTS code you posted like this.

SELECT EXISTS (
    SELECT
        *
    FROM
        friend_list AS users
    INNER JOIN
        friend_list AS friends
            ON users.friend_id = friends.user_id
    WHERE
        users.user_id = 1
        AND friends.friend_id = 3
) AS result
+1  A: 

The query you already wrote is along the right lines, I think you need one less join to the friend-list table:

select distinct fl1.user_id, fl2.friend_id
from friend_list fl1
inner join friend_list fl2 on fl1.friend_id = fl2.user_id
where fl1.user_id = 1 and fl2.friend_id = 3

So '1' (fl1.user_id) is friends with 'x' (fl1.friend_id and fl2.user_id) who is friends with '3' (fl2.friend_id).

Because you have two complementary entries in friend-list for each pair of friends, the query is nice and simple. It would be a bit tougher if each pair of friends only got one row in friend-list ....

(edit: realised too many joins were happening...)
(edit: added a distinct into the select after comment conversation)

codeulike
I have that 4 is also friend of 1.So the query is returning 2 rows. So if the query does not return any rows it means that 3 is not a friend of a friend of 1. And if the query returns one or more rows it means 3 is a friend of a friend of one? Is there a more efficient way so that the query just returns one or cero rows?
Enrique
You mean, if they have multiple friends in common, you only want 1 row to come back. Sure, use a DISTINCT in the SELECT clause ... I will edit my answer ...
codeulike
No. 4 is not a common friend is just a friend of 1.
Enrique
Hmm, in that case the original query should only have returned 1 row. If 4 has no link to 3 then the where clause should filter 4 out of the result set. Are you sure you got 2 rows back?
codeulike
Yes it returned two rows
Enrique
What was in the result set, what numbers did it return? (when run with a "select *")
codeulike
I would check both the source data and the output data. If [1 is a friend of 4] but [4 is *not* a friend of 3], this combination won't yield a result in codeulike's answer. There must be something else going on in the data.
Dems
This is the data in the friend_list table12,14,21,23,31,41and te result was 2 rowsuser_id friend_id user_id friend_id user_id friend_id1 2 1 2 2 3,1 4 1 2 2 3
Enrique
The result with codeulike code wasuser_id friend_id1 3
Enrique
The result that gave 2 rows is from your own sql example then? The one with two joins instead of just 1? Short answer is: the code is incorrect. You have: "fl1 INNER JOIN fl2 ON fl1.user_id = fl2.user_id" and "WHERE fl1.user_id = 1". This gives "12-12" and "12-14" and "14-12" and "12-14". codeulike's example doesn't have this error and so works fine.
Dems
+1  A: 

As you're looking to find out if there are Any instances of "a and b have a friend in common" you're better of using the EXISTS keyword.

The benefit of this over using COUNT or DISTINT is that the optimiser knows a couple of things:
1. The actual data doesn't matter
2. It can stop searching after the first hit

For example...

IF EXISTS (
    SELECT
        *
    FROM
        friend_list AS [user]
    INNER JOIN
        friend_list AS [friend]
            ON [user].friend_id = [friend].user_id
    WHERE
        [user].user_id = @user_id
        AND [friend].friend_id = @friend_of_friend_id
)
BEGIN
    RETURN 1
    -- Or whatever code you want to execute
END
ELSE
BEGIN
    RETURN 0
    -- Or whatever code you want to execute
END

Although this doesn't have "TOP 1" and uses "*", it neither actually returns multiple fields or rows. It literally just searches for the existance of a match and stops when it finds one.

Dems
Ok I will try your solution. Im new to db what does the @ sign mean?
Enrique
@user_id and @friend_of_friend_id are just variables.
Dems