I was building a feed system ala facebook just for the fun and training of it but I came up with an interesting problem. Imagine this scenario:
I am friend with nr1, nr2 and nr3. In my database it is also a user, nr4 in the database who I'm not friend with. So let say nr1 befriend nr4, in that case I want my feed to get updated with nr1 is friend with nr4. This is basic not very hard to do (at least to get it working, I don't no s*** about performance :P).
Then nr1 befriend nr2 but this is equivalent with nr2 befriend nr1 and that's my problem.
My friendship database works in the way that one friendship generates two records. I've read that's usually the faster way to do it.
Friendship table:
user_id
friend_id
pending
lastchange
User table:
user_id
user_name
I've came up with the following code:
SELECT
u1.user_id AS friend_id,
u1.user_name SA friend_name,
u2.user_id AS friends_friend_id,
u2.user_name AS friends_friend_name
FROM users AS u1
JOIN friendship AS f1
ON u1.user_id = f1.friend_id
&& f1.user_id = {$user_id}
&& f1.friend_pending = 0
JOIN friendship AS f2
ON u1.user_id = f2.user_id
&& f2.friend_pending = 0
JOIN users AS u2
ON f2.friend_id = u2.user_id
WHERE TIMESTAMPDIFF(SECOND, f2.lastchange, '{$last_login}' ) < 0;
Okey what this basically does is that it get all the users I'm friend with who befriend other people since my last login. The only problem is that if I'm friend with two people who befriend each other both of them will be selected in this query.
The difference (which makes it impossible to use SELECT DISTINCT) is that;
the first time nr1 will be the friend and nr 2 the friends friend and
the second time nr2 will be the friend and nr1 will be the friends friend.
I don't want that, I want one row per relationship.
You guys have any idea how to solve this? Any solution I came up with has ended with nothing being displayed :(
Thanks!
1st edit: I just came up with another problem which also is connected to this problem. In phpMyAdmin the above generate a correct table with the 4 columns. But in php (or rather codeigniter's query function) it only show two columns but adds more rows... this end up in I have no idea who befriend who..
2nd edit again: Okey I just solved the problem I presented in the first edit, I simply renamed the columns with AS. Still having the primary problem thou.
3rd edit: Stevie G was requesting some sample data, this is query result:
friend_id | friend_name | friends_friend_id | friends_friend_name
1 nr1 4 nr4
1 nr1 2 nr2
2 nr1 1 nr2
This is what I get if you follow my little story. the id = 1 is not "me", it's another user
4th edit Unfortunately I wasn't quite clear enough I think. What I want to get in the end is an array where each element contains:
friend_id
friend_name
friends_friend_id
friends_friend_name
Just like the sample data from edit 3 except the names I had before might have mislead you..
So my problem is, as you can see edit 3, that if two of my friends became friends I will get two rows for one relationship. What I want to get would be:
friend_id | friend_name | friends_friend_id | friends_friend_name
1 nr1 2 nr2
2 nr1 1 nr2
I hope I made myself clearer! Thanks
So to me it looks like your requirements are incomplete, in that in the friend feed, if nr2 and nr1 befriend each other, do you want to:
See something that says "Your mutual friends nr2 and nr1 befriended each other!"
See something that says "Your friend nr2 befriended nr1!" based on who actually reached out for friendship first.
Scenario #1
You need another join checking if your friends' friends are also in your friends and if so, returning a boolean column named AlsoMyFriend. Then if AlsoMyFriend is 1, return a "Your mutual friends befriended each other", otherwise return a normal "Your friend befriended someone new."
Alternatively, you could do this in code, by simply checking to see if your friends' friends' ID is in your array of friends' IDs and flipping your feed statement based on that.
Scenario #2
Add a column to the friendship table *primary_mover* and set that value to the *user_id* that initiated the friendship. When looking up friends' friends filter for *where primary_mover = user_id* to only get 1 of the 2-sided relationship.
As a simple measure, you could simply add a filter to say only pull friends' friends where the friends' friend ID is greater than my friends' ID:
SELECT
u1.user_id AS friend_id,
u1.user_name SA friend_name,
u2.user_id AS friends_friend_id,
u2.user_name AS friends_friend_name
FROM users AS u1
JOIN friendship AS f1
ON u1.user_id = f1.friend_id
&& f1.user_id = {$user_id}
&& f1.friend_pending = 0
JOIN friendship AS f2
ON u1.user_id = f2.user_id
&& f2.friend_pending = 0
&& f2.friend_id > f2.user_id -- pulls nr1 befriending nr2, not nr2 befriending nr1
JOIN users AS u2
ON f2.friend_id = u2.user_id
WHERE TIMESTAMPDIFF(SECOND, f2.lastchange, '{$last_login}' ) < 0;
Did you try using
SELECT DISTINCT
This is a tricky one do do in your head, but, try this:
SELECT --Select your user details, and your direct friends details
u1.user_id UserId,
u1.user_name UserName,
u2.user_id FriendsUserId,
u2.user_name FriendsUserName
FROM users AS u1
JOIN friendship AS f1
ON u1.user_id = {$user_id}
&& f1.friend_pending = 0
JOIN users AS u2
ON f1.friend_id = u2.user_id
JOIN friendship AS f2
ON f1.friend_id = f2.userid
&& f2.friend_pending = 0
WHERE TIMESTAMPDIFF(SECOND, f2.lastchange, '{$last_login}' ) < 0;
UNION
SELECT --Select your user details, and your friends friends details
u3.user_id UserId,
u3.user_name UserName,
u4.user_id FriendsUserId,
u4.user_name FriendsUserName
FROM users AS u3
JOIN friendship AS f3
ON u3.user_id = {$user_id}
&& f3.friend_pending = 0
JOIN friendship AS f4
ON f3.friend_id = f4.user_id
&& f4.friend_pending = 0
JOIN users AS u4
ON f3.friend_id = u4.user_id
WHERE TIMESTAMPDIFF(SECOND, f4.lastchange, '{$last_login}' ) < 0;
Assuming the results are as I think they are, the UNION should filter out the dupes as u1.user_id should always be your user_id..
Try
SELECT
u1.user_id AS friend_id,
u1.user_name SA friend_name,
u2.user_id AS friends_friend_id,
u2.user_name AS friends_friend_name
FROM users AS u1
JOIN friendship AS f1
ON u1.user_id = f1.friend_id
&& f1.user_id = {$user_id}
&& f1.friend_pending = 0
JOIN friendship AS f2
ON u1.user_id = f2.user_id
&& f1.user_id <> f2.friend_id
&& f2.friend_pending = 0
JOIN users AS u2
ON f2.friend_id = u2.user_id
WHERE TIMESTAMPDIFF(SECOND, f2.lastchange, '{$last_login}' ) < 0;
Related
I hope you are well :).
I'm a little bit stuck with a problem in sql that I can't solve. I would like to get all the people who are not yet friends with a user without tuple.
That is to say that in my column user_id or contact_user_id, it would be necessary that if: user_id = 1 and contact_user_id = 2 (they are friends) that it does not show for the user 2 that it is not friends with user 1.
It should display for user 2 that he is missing user 3,4 and not 1 nor himself
I have for the moment a request of the kind but it does not display the good expected result.
Thanks a lot to the person who will take the time to answer
Their is the desired result without the first line (cause their are already friend) :
I got for the moment this sql statement :
select us.user_id, us.login
from user us where u.user_id!=2
and not exists (select 1 from contact ctc
where ctc.contact_user_id = us.user_id
and ctc.user_id = 2);
EDIT : I maybe founded the sql request
select u.*
from user u
where not exists (select 1
from contact c
where (c.user_id = u.user_id and c.contact_user_id = 2) or (c.contact_user_id= u.user_id and c.user_id=2)
);
If you want records where there is no reciprocal relationship (as your description implies), you can use not exists:
select c.*
from contact c
where not exists (select 1
from contact c2
where c2.contact_user_id = c.user_id and
c.user_id = c.contact_user_id
);
If you want additional information from another table, just join in the outer query:
select c.*
from contact c join
user u
on c.user_id = u.user_id
where not exists (select 1
from contact c2
where c2.contact_user_id = c.user_id and
c.user_id = c.contact_user_id
);
Let's say I have a table called friends, and for each friendship, I add two entries. For example, if users 1 and 2 are friends, we will have:
uid1 uid2
----------
1 2
2 1
My goal is to find friends of friends, exclding the friends. The following query gives me friends of friends (including friends):
SELECT f2.uid2 FROM friends f1, friends f2 WHERE f1.uid1='YOUR_ID' AND f1.uid2=f2.uid1 AND f2.uid2!='YOUR_ID'
My preference is not to use IN or NOT IN. Can you think of anything to supplement this query?
Add an outer join with your own friends so you can filter them out.
SELECT f2.uid2 FROM friends f1
JOIN friends f2 ON f1.uid2 = f2.uid1
LEFT JOIN friends f3 ON f3.uid2 = f2.uid2 AND f3.uid1 = 'YourID'
WHERE f1.uid1='YourID'
AND f2.uid2!='YourID'
AND f3.uid2 IS NULL
SQLFIDDLE
You can do it with an additional join and aggregation:
SELECT f2.uid2
FROM friends f1 join
friends f2
on f1.uid1 = 'YOUR_ID' AND f1.uid2 = f2.uid1 AND f2.uid2 <> 'YOUR_ID'` join
friends f3
on f2.uid2 = f3.fuid1
GROUP BY f2.uid2
HAVING sum(f3.uid2 = 'YOUR_ID') = 0;
want mysql query for finding mutual friend between two friend but
I am maintain the friendship of user in one way relationship for ex.
first is users table
id name
1 abc
2 xyz
3 pqr
Now second table is friend
id user_id friend_id
1 1 2
2 1 3
3 2 3
Now here i can say that abc(id=1) is friend of xyz(id=2) now similar way the xyz is friend of abc but now i want to find mutual friend between abc(id=1) and xyz(id=2) that is pqr so I want mysql query for that.
REVISED
This query will consider the "one way" relationship of a row in the friend table to be a "two way" relationship. That is, it will consider a friend relationship: ('abc','xyz') to be equivalent to the inverse relationship: ('xyz','abc'). (NOTE: we don't have any guarantee that both rows won't appear in the table, so we need to be careful about that. The UNION operator conveniently eliminates duplicates for us.)
This query should satisfy the specification:
SELECT mf.id
, mf.name
FROM (
SELECT fr.user_id AS user_id
, fr.friend_id AS friend_id
FROM friend fr
JOIN users fru
ON fru.id = fr.user_id
WHERE fru.name IN ('abc','xyz')
UNION
SELECT fl.friend_id AS user_id
, fl.user_id AS friend_id
FROM friend fl
JOIN users flf
ON flf.id = fl.friend_id
WHERE flf.user IN ('abc','xyz')
) f
JOIN users mf
ON mf.id = f.friend_id
GROUP BY mf.id, mf.name
HAVING COUNT(1) = 2
ORDER BY mf.id, mf.name
SQL Fiddle here http://sqlfiddle.com/#!2/b23a5/2
A more detailed explanation of how we arrive at this is given below. The original queries below assumed that a row in the friend table represented a "one way" relationship, in that "'abc' ff 'xyz'" did not imply "'xyz' ff 'abc'". But additional comments from the OP hinted that this was not the case.
If there is a unique constraint on friend(user_id,friend_id), then one way to get the result would be to get all of the friends of each user, and get a count of rows for that friend. If the count is 2, then we know a particular friend_id appears for both user 'abc' and for 'xyz'
SELECT mf.id
, mf.name
FROM friend f
JOIN users uu
ON uu.id = f.user_id
JOIN users mf
ON mf.id = f.friend_id
WHERE uu.name IN ('abc','xyz')
GROUP BY mf.id, mf.name
HAVING COUNT(1) = 2
ORDER BY mf.id, mf.name
(This approach can also be extended to find a mutual friend of three or more users, by including more users in the IN list, and changing the value we compare the COUNT(1) to.
This isn't the only query that will return the specified resultset; there are other ways to get it as well.
Another way to get an equivalent result:
SELECT u.id
, u.name
FROM ( SELECT f1.friend_id
FROM friend f1
JOIN users u1
ON u1.id = f1.user_id
WHERE u1.name = 'abc'
) t1
JOIN ( SELECT f2.friend_id
FROM friend f2
JOIN users u2
ON u2.id = f2.user_id
WHERE u2.name = 'xyz'
) t2
ON t2.friend_id = t1.friend_id
JOIN users u
ON u.id = t1.friend_id
ORDER BY u.id, u.name
NOTES
These queries do not check whether user 'abc' is a friend of 'xyz' (the two user names specified in the WHERE clause). It is only finding the common friend of both 'abc' and 'xyz'.
FOLLOWUP
The queries above satisfy the specified requirements, and all the examples and test cases provided in the question.
Now it sounds as if you want a row in that relationship table to be considered a "two way" relationship rather than just a "one way" relationship. It sounds like you want to want to consider the friend relationship ('abc','xyz') equivalent to ('xyz','abc').
To get that, then all that needs to be done is to have the query create the inverse rows,, and that makes it easier to query. We just need to be careful that if both those rows ('abc','xyz') and ('xyz','abc') already exist, that we don't create duplicates of them when we invert them.
To create the inverse rows, we can use a query like this. (It's simpler to look at this when we don't have the JOIN to the users table, and we use just the id value:
SELECT fr.user_id
, fr.friend_id
FROM friend fr
WHERE fr.user_id IN (1,2)
UNION
SELECT fl.friend_id AS user_id
, fl.user_id AS friend_id
FROM friend fl
WHERE fl.friend_id IN (1,2)
It's simpler if we don't include the predicates on the user_id and friend_id table, but that could be a very large (and expensive) rowset to materialize.
try this:
given that you want to get the mutual friends of friends 1 & 2
select friend_id into #tbl1 from users where user_id = 1
select friend_id into #tbl2 from users where friend_id = 2
select id, name from users where id in(select friend_id from #tbl1 f1, #tbl2 f2 where f1.friend_id=f2.friend_id)
Im building a platform where users can connect with other users (Social platform)
I have a Table called friends and i am saving the connections like this
user_id | friend_id | request | add_date
Now i need to write a sql query to get the most recent friends a specific user's friends added that they are or not already friends of the user. Also the user must be accepted.
Think of it as a news feed and i was to see who my friends recently added (the new recently add person can be my friend or not)
So far i have this but works only when my friend added people i already have.
SELECT user_main_id AS frmname, friend_id AS type_id, add_date AS date
FROM friends
WHERE friend_id
IN
(SELECT friend_id
FROM friends WHERE (friend_id='$user_id' OR user_main_id='$user_id')
AND request=1 AND friend_id!=$user_id)
AND request=1 AND friend_id!=$user_id AND user_main_id!=$user_id
ORDER BY date DESC
Maybe there is a better why to approach this.
Suggestions? Much appreciated thanks. The connection is bilateral, no difference between user_id and friend_id. Was designed with those names and had to be carried forward.
Sample Record
96618 50683 1 2013-05-08 13:44:31
96618 1230 1 2013-04-03 18:28:51
11671 96618 1 2013-04-03 13:26:51
11671 1230 1 2013-03-23 18:26:08
Once 96618 connects with 50683 happens. users 11671 for example will get a msg saying your friend 96618 is now friends with 50683
Try this:
SELECT f2.friend_id
FROM friends f1 # Friends of target user
INNER JOIN friends f2
ON f1.friend_id = f2.user_id # Friends of their friends
AND f2.request = 1
AND f2.friend_id != f1.user_id
INNER JOIN friends f3 # Limiting it to mutual friends
ON f2.friend_id = f3.user_id AND f3.friend_id = f1.user_id
WHERE f1.user_id = 11671 AND f1.request = 1
ORDER BY f2.add_date DESC
Please note that with your current table structure, you are going to need two rows for each friendship, one for each direction. To do it with only one row, you probably want to split it into two tables - friendships and friendship_members.
I am creating a small social network in which when a user posts anything.I will get ids of his friends to whom he referred the post.Now the problem is that the users who will be referred can be between 1 or 10. So now suppose that a user posted a status and referred 6 friends.
Now i need to find mutual friends between the user who posted the status and the people referred one by one.Meaning that suppose user who posted the status has id 1 and the people who are referred have 2,3,4,5,6,7.
So what the query will do it will find mutual friends between 1 and 2 then 1 and 3 then 1 and 4 and so on till 7.So at the end i will be getting a result containing the mutual friends between the user who posted and the people who are referred(or tagged we can say).
I've two tables User and Friends
User Table: Friends Table:
-------------------------- -----------------------------------
UserID Name friendsid friendname userid
-------------------------- -----------------------------------
1097517748 User1 3 friend1 536772587
100003052455644 User2 8 friend2 100003052455644
536772587 User3 8 friend3 1097517748
4 User4 3 friend4 100003052455644
Now the friends table can contain different people who does or does not exist in the user table.In this example user 2 and 3 has friend with id 8 in common.I've got my guy but that guy id i.e 8 must be present in the user table also.This is only between userid 2 and 3.
I will be getting several userid and i've find mutual friends of all the friends that are referred with the guy who has posted the status.
I hope I've cleared it.If I get the idea for the query I can generate it dynamically using php for different number of referred friends.
So far I've written this query.
SELECT count(f1.friendsid) FROM friends AS f1
INNER JOIN friends AS f2 ON f1.friendsid = f2.friendsid
WHERE f1.userid = 100003052455644
AND f2.userid = 1097517748
But this is only bringing me mutual friends between two user.When I add a third guy lets say f3 and compare it with f1 it does not bring any result and another problem with this query is that it is not verifying that the mutual friend id exists in the user table or not.After adding the 3rd guy.
SELECT count(f1.friendsid) FROM friends AS f1
INNER JOIN friends AS f2 ON f1.friendsid = f2.friendsid
INNER JOIN friends AS f3 ON f1.friendsid = f3.friendsid
WHERE f1.userid = 100003052455644
AND f2.userid = 1097517748
AND f3.userid = 536772587
This query does not bring any results.So I wanted to create a query that does all this.
EDIT:
Actually I am using facebook connect and I save all the users friends in my database when users login, the user table will contain only those users who are registered on my website.
I finally figured it out. The query was
SELECT DISTINCT(friendsid) FROM (
SELECT DISTINCT(f1.friendsid) FROM friends AS f1
INNER JOIN friends AS f2 ON f1.friendsid=f2.friendsid
and f1.userid = 100003052455644 and f2.userid =1097517748
INNER JOIN user ON f2.friendsid = user.userid and f1.friendsid = user.userid
UNION ALL
SELECT (f1.friendsid) FROM friends AS f1
INNER JOIN friends AS f2 ON f1.friendsid=f2.friendsid
and f1.userid = 100003052455644 and f2.userid =536772587
INNER JOIN user ON f1.friendsid = user.userid and f2.friendsid = user.userid
UNION ALL
SELECT DISTINCT(f1.friendsid) FROM friends AS f1
INNER JOIN friends AS f2 ON f1.friendsid=f2.friendsid
and f1.userid = 100003052455644 and f2.userid =694250830
INNER JOIN user ON f1.friendsid = user.userid and f2.friendsid = user.userid
) a
This query will find the id's of all the mutual friends between the user who posted the status and the users whom he refered.
The user with id 100003052455644 is the person who posted status and all the others are his friends whom he referred.The DISTINCT will make sure that the id's are unique and not repeated.This is for 3 persons he referred.We can add the UNION ALL part dynamically setting the id's and this will help us create a dynamic query for retrieving mutual friends for different number of referred people.