"Friend" relationships across two mysql tables - mysql

I'm not quite sure how to construct an sql join inorder to find who a specific users "friends" are.
For example I have two table
User Table
u_ID | u_Name
-------------
1 | bob
2 | jill
3 | jack
4 | susan
Friends Table
f_ID | u_ID1 | u_ID2
--------------------
1 | 1 | 2
2 | 3 | 1
3 | 4 | 2
I need to find a way of getting all of bobs friends, or all of jills friends, for example.
Friends cannot have duplicate results
I.e. can have a row with either (u_ID1 = 1, u_ID2 = 2) or (u_ID1 = 2, u_ID = 1) but not both, as they are technically the same.
Here is my incorrect query
SELECT u.u_Name
FROM user u
INNER JOIN friends f ON (f.u_ID1 = '1' OR f.u_ID2 = '1')
Thanks in advance
Solution
Check Kris Babic reply for solution,
also thank you for everyone elses help

This uses a STRAIGHT join, but should work:
select u.u_Name
from friends f, user u
where (f.u_ID1 = '1' and u.u_ID = f.u_ID2)
or (f.u_ID2 = '1' and u.u_ID = f.u_ID1)

For all of bob's friends this should work (untested)
select u.u_Name
FROM user u
inner join friends f1 on f1.u_uID1 = u.u_ID
inner join friends f2 on f2.u_uID2 = u.u_ID
where u.u_ID = 1

Try this:
SELECT u1.u_Name as user1 , u2.u_Name as user2
FROM user as u1 INNER JOIN friends ON u1.u_ID=friends.u_ID1
INNER JOIN user as u2 ON u2.u_ID=friends.u_ID2

Related

Join 3 tables only if they have information on 1 table

I have 3 table
| Likes | | Friends | | Item |
----------------------------- ------------------ -------------------
| UserID | LikeID | itemID | | user1 | user2| | itemID |
I would like to join the 3 tables based on if the user has liked an item in the Likes table and the user must also be friends with the user in the Friends table and both of them have liked the item.
The way the Friends table is set up is if a user adds a friend the userID will be placed in either user1 or user2 depending who requested the friend first, so the userID of say 1 can be in either user1 column or user2 column and users friend will be in the opposing column.
Then I would do an inner join on itemID table to get the item both of the users liked.
I am no SQL expert so I am finding this very difficult and any code examples I give you would be rubbish. I have tried all types of left joins inner joins right joins I've joined the crap out of these tables but I do not get the correct data.
Any steer in the right direction on how to accomplish this would be great I am out of ideas. Please let me know of any further information I can give you.
I believe this does what you are after:
select a.user1, b.likeid, a.user2, c.likeid, d.itemid
from friends a
inner join likes b on a.user1 = b.userid
inner join likes c on a.user2 = c.userid
inner join item d on (b.itemid = d.itemid and c.itemid = d.itemid);
Here's an sqlfiddle demonstrating it: http://sqlfiddle.com/#!4/ff9d59/11

MySQL Many to Many query confusion

I've looked at a bunch of questions and solutions regarding many to many queries. I just can't seem to wrap my head around it. Maybe I'm not completely understanding the keywords in MySQL. But...
I have 3 tables. The first table is a list of peoples contact information. The second table is a list of mailing list categories. The third table is an associative table that holds the id's from the first and second table. How would I write a MySQL query to get all the contacts from the contact table that match the VIP list id (which I already have)?
Table 1 (contacts)
id | name | email
-----------------------------
1 | John | john#gmail.com
-----------------------------
2 | Jane | jane#gmail.com
-----------------------------
Table 2 (list_type)
id | list_name |
-----------------
1 | VIP's |
-----------------
2 | Generic |
-----------------
Table 3 (list_contact_joiner)
contact_id | list_type_id |
----------------------------
1 | 2 |
----------------------------
2 | 1 |
----------------------------
This is what I tried but get a syntax error
$listID = 1;
SELECT list_contact_joiner.contact_id
FROM list_contact_joiner
WHERE list_id = $listID AS lcj
INNER JOIN contact_lists AS cl
ON cl.id = lcj.contact_id
SELECT c.*
FROM contacts c
JOIN list_contact_joiner j on j.contact_id = c.id
JOIN list_type t on j.list_type_id = t.id
WHERE t.list_name = 'VIP''s'
If you already have the id of VIP's then you need to join only 2 tables
SELECT c.*
FROM contacts c
JOIN list_contact_joiner j on j.contact_id = c.id
WHERE j.list_type_id = 1
Yes the join statement is not correct. It should be something as
select
c.name,
c.email,
lt.list_type
from list_contact_joiner lcj
join contacts c on c.id = lcj.contact_id
join list_type lt on lt.id = lcj.list_type_id
where
lt.id = ?
If you are looking for data with $listID = 1; then the place holder is
lt.id = ?
Your syntax error is because all the table specification (FROM and JOIN) has to occur before the WHERE and you got your alias syntax a little messed up - this should work better:
$listID = 1;
SELECT lcj.contact_id
FROM list_contact_joiner AS lcj
INNER JOIN contact_lists AS cl
ON cl.id = lcj.contact_id
WHERE lcj.list_id = $listID
But if all you are trying to do is get the contact_id then you don't need to do any join at all...
SELECT contact_id
FROM list_contact_joiner
WHERE list_id = $listID
If you need the rest of the contact information then you might try this:
SELECT contact.*
FROM list_contact_joiner
JOIN contacts ON contacts.id = list_contact_joiner.contact_id
WHERE list_id = $listID
Note that you still don't need to get the list_type table at all.

get friends of friend using mysql

I am making a query in which i want to get friends of friends but the problem is that if my friends are also friends it suggesting their ids too . Suppose user id is 1 and he has teo friends 2 and 3 and 2 has is friends with 4 and 3 and 3 is friends with 5 and 2 then its suggesting me 4,5 which it should but also suggesting 2,3 because they are friends and they both are also my friends which is wrong it should only suggest 4,5
My query is
SELECT fr.friend_id
FROM friend_list fl ,friend_list fr
WHERE fl.login_id='27'
AND fl.status='3'
AND fl.friend_id=fr.login_id
AND fr.status='3'
AND fl.login_id!=fr.friend_id
One way to do this is to add WHERE NOT IN predicate :
SELECT fr.friend_id
FROM friend_list fl
INNER JOIN friend_list fr ON fl.friend_id = fr.login_id
AND fl.status = fr.status
WHERE fl.login_id = 27 AND fl.status = '3'
AND fr.friend_id NOT IN(SELECT friend_id
FROM friend_list
WHERE login_id = 27 AND friend_id IS NOT NULL)
The subquery after the NOT IN will select the friends of the friend id you are passing. to exclude them.
SQL Fiddle Demo
This will give you only 4, 5 for the example you gave in your question:
| FRIEND_ID |
|-----------|
| 4 |
| 5 |
Note that: I used the explicit JOIN syntax instead of the old JOIN syntax you are using, it should be the same but it is recommended to use the new one.

MySQL Query to find friends and number of mutual friends

I have looked through the questions but I cant find anything that does exactly what I need and I can't figure out how to do it myself.
I have 2 tables, a user table and a friend link table. The user table is a table of all my users:
+---------+------------+---------+---------------+
| user_id | first_name | surname | email |
+---------+------------+---------+---------------+
1 joe bloggs joe#test.com
2 bill bloggs bill#test.com
3 john bloggs john#test.com
4 karl bloggs karl#test.com
My friend links table then shows all relationships between the users, for example:
+--------=+---------+-----------+--------+
| link_id | user_id | friend_id | status |
+---------+---------+-----------+--------+
1 1 3 a
2 3 1 a
3 4 3 a
4 3 4 a
5 2 3 a
6 3 2 a
As a note the a in the status column means approved, there could also be r(request) and d(declined).
What I want to do is have a query where if a user does a search it will bring back a list of users that they are currently not already friends with and how many mutual friends each user has with them.
I have managed to get a query for all users that are currently not friends with them. So if the user doing the search had the user id of 1:
SELECT u.user_id,u.first_name,u.surname
FROM users u
LEFT JOIN friend_links fl
ON u.user_id = fl.user_id AND 1 IN (fl.friend_id)
WHERE fl.friend_id IS NULL
AND u.user_id != 1
AND surname LIKE 'bloggs'
How then do I have a count of the number of mutual friends for each returned user?
EDIT:
Just as an edit as I don't think I am being particularly clear with my question.
The query that I currently have above will produce the following set of results:
+---------+------------+---------+
| user_id | first_name | surname |
+---------+------------+---------+
2 bill bloggs
4 karl bloggs
Those are the users matching the surname bloggs that are not currently friends with joe bloggs (user id 1).
Then I want to have how many mutual friends each of these users has with the user doing the search so the returned results would look like:
+---------+------------+---------+--------+
| user_id | first_name | surname | mutual |
+---------+------------+---------+--------+
2 bill bloggs 1
4 karl bloggs 1
Each of these returned users has 1 mutual friend as joe bloggs (user id 1) is friends with john bloggs and john bloggs is friends with both returned users.
I hope this is a bit more clear.
Thanks.
Mutual friends can be found by joining the friend_links table to itself on the friend_id field like so:
SELECT *
FROM friend_links f1 INNER JOIN friend_links f2
ON f1.friend_id = f2.friend_id
WHERE f1.user_id = $person1
AND f2.user_id = $person2
But bear in mind that this, in its worst case, is essentially squaring the number of rows in the friend_links table and can pretty easily jack up your server once you have a non-trivial number of rows. A better option would be to use 2 sub-queries for each user and then join the results of those.
SELECT *
FROM (
SELECT *
FROM friend_links
WHERE user_id = $person1
) p1 INNER JOIN (
SELECT *
FROM friend_links
WHERE user_id = $person1
) p2
ON p1.friend_id = p2.friend_id
Also, you can simplify your friend_links table by removing the surrogate key link_id and just making (user_id,friend_id) the primary key since they must be unique anyway.
Edit:
How would this be applied to the original query of searching for users that aren't already friends, I would like to do both in a single query if possible?
SELECT f2.user_id, COUNT(*) 'friends_in_common'
FROM friend_links f1 LEFT JOIN friend_links f2
ON f1.friend_id = f2.friend_id
WHERE f1.user_id = $person
GROUP BY f2.user_id
ORDER BY friends_in_common DESC
LIMIT $number
I am also thinking that the user_id constraints can be moved from the WHERE clause into the JOIN conditions to reduce the size of the data set created by the self-join and preclude the use of subqueries like in my second example.
This query lists anyone who's not friend with user 1 and whose surname matches '%bloggs%':
SELECT
users.user_id,
users.first_name,
users.surname,
Sum(IF(users.user_id = friend_links_1.friend_id, 1, 0)) As mutual
FROM
users inner join
(friend_links INNER JOIN friend_links friend_links_1
ON friend_links.friend_id = friend_links_1.user_id)
ON friend_links.user_id=1 AND users.user_id<>1
WHERE
users.surname LIKE '%bloggs%'
GROUP BY
users.user_id, users.first_name, users.surname
HAVING
Sum(IF(users.user_id = friend_links.friend_id, 1, 0))=0
just change the user id on the ON clause, and the surname on the WHERE clause. I think it should work correctly now!
If A is friend of B, then B is also a friend of A? Wouldn't it be better to use just a link instead of two links (and instead of two rows in friends_links)? Then you have to use two status columns, status1 and status2, and A is friend of B only if status1 = status2 = "a".
There are many ways to show mutual friends, e.g.:
SELECT friend_id
FROM friend_links
WHERE friend_links.user_id = $user1 or friend_links.user_id = $user2
AND NOT (friend_links.friend_id = $user1 or friend_links.friend_id = $user2)
GROUP BY friend_id
HAVING Count(*)>1
And this query shows for each user and anyone who's not his/her friend:
SELECT
users.user_id,
users.first_name,
users_1.user_id,
users_1.first_name
FROM
users INNER JOIN users users_1 ON users.user_id <> users_1.user_id
WHERE
NOT EXISTS (SELECT *
FROM friend_links
WHERE
friend_links.user_id = users.user_id
AND friend_links.friend_id = users_1.user_id)
(The only think I didn't check is the friendship status, but it's easy to add that check).
I'm still working on it, but it's not easy to combine nicely these two queries togheter. So this isn't exactly an answer, I'm just showing some ideas that i've tried.
But what do you need exactly? A query that returns every user with anyone who's not his/her friend and the number of friends in common, or is the user_id already given?
With some code it's not a problem to answer your question... but there has to be a nice way just by using SQL! :)
EDIT:
I'm still wondering if there's a better solution to this, in particular the next query could be extremely slow, but it looks like this might work:
SELECT
users_1.user_id,
users_2.user_id,
Sum(IF(users_1.user_id = friend_links.user_id AND users_2.user_id = friend_links_1.friend_id, 1, 0)) As CommonFriend
FROM
users users_1 INNER JOIN users users_2
ON users_1.user_id <> users_2.user_id,
(friend_links INNER JOIN friend_links friend_links_1
ON friend_links.friend_id = friend_links_1.user_id)
GROUP BY
users_1.user_id,
users_2.user_id
HAVING
Sum(IF(users_1.user_id = friend_links.user_id AND users_2.user_id = friend_links.friend_id, 1, 0))=0
(as before, i didn't check friendship status)
If user is given, you could put WHERE users_1.user_id=$user1 but it's better to just leave one user table, and filter the next INNER JOIN whith that user.

two way joins mysql query

message table
id user_id| message
1 1 | this is my cruel message
2 1 | this is my happy message
3 2 | this is happy messgae
message_tags table
id message_id| tags
1 2 | happy
2 3 | happy
what i want to acess all the messages that have the the tag happy, how would construct the query in the best possible way :)) thanks
p.s. this is just an example database
select m.id, m.user_id, m.message,
u.Username
from message m
inner join user_table u on m.user_id = u.id
where m.id in (select message_id from message_tags where tags = 'happy')