'friends of friends' SQL query - mysql

This question is related to previous one. You can check my first post here
I'm trying to pull data from a user table and I need 'friends of friends', those who are two steps away from the chosen user but not directly connected to the chosen user
I tried with this query:
select
u.*
from user u
inner join friend f
on u.user_id = f.friend_id
inner join friend ff
on f.user_id = ff.friend_id
where ff.user_id = {$user_id} AND u.user_id <> {$user_id};
I didn't know how to pull users who are not directly connected to the chosen user. I get all friends of friends of the current user, but I also get direct friends of the current user.

You just need to exclude the ones who are direct friends as well as being friends-of-friends. I've rearranged the table aliases so it's a bit clearer (to me, anyway) what's being retrieved:
SELECT
u.*
FROM
user u
INNER JOIN friend ff ON u.user_id = ff.friend_id
INNER JOIN friend f ON ff.user_id = f.friend_id
WHERE
f.user_id = {$user_id}
AND ff.friend_id NOT IN
(SELECT friend_id FROM friend WHERE user_id = {$user_id})
It also removes the need to exclude the user ID being queried.

You are on the right track. You need to add a where condition that excludes those who are directly related:
select u.*
from user u
inner join friend f on u.user_id = f.friend_id
inner join friend ff on f.user_id = ff.friend_id
where ff.user_id = {$user_id} AND u.user_id <> {$user_id};
AND not exists
(select f2.friend_id
from friend f2
where f2.friend_id = ff.friend_id
and u.user_id = f2.user_id)
The extra not exists clause that I added to your query verifies that the second degree friend is also not a first degree friend.

I'd use a non-correlated subquery, which retrieves the ids of the direct friendships of {$user_id}, and exclude these users from the final result:
select u.*
from user u
inner join friend f on u.user_id = f.friend_id
inner join friend ff on f.user_id = ff.friend_id
where ff.user_id = {$user_id} AND u.user_id <> {$user_id}
AND u.user_id not in (
select directFriend.friend_id
from friend directFriend
where directFriend.user_id = {$user_id})

Related

SQL query to find all users, User 'A' follows and then check if another User 'B' also follows the same users

I want to display all users data, who User 'A' is following. And then further check if User 'B' is also following some users of User 'A'.
I managed to get al users data, who User 'A' is following. But don't understand how to query for the second condition.
Here is my Fiddle link with an example: https://dbfiddle.uk/?rdbms=mysql_5.7&fiddle=29a7d1e29f794a8f18a89fe45c06eaa9
You can try to let your User 'B' in a subquery then do OUTER JOIN
SELECT u.*,
IF(friend_id IS NULL,0,1) amIfollowing
FROM users u
LEFT JOIN (
Select friend_id
from friends
where user_id = 5
) f ON f.friend_id = u.id
WHERE u.id IN (SELECT f.friend_id
FROM friends f
WHERE f.user_id = 1)
ORDER BY u.id
sqlfiddle
If I understand correctly you can try to use only one subquery for friends and then use the condition aggregate function to get the result.
SELECT u.id,
u.image_width,
MAX(CASE WHEN f.user_id = 5 THEN 1 ELSE 0 END) amIfollowing
FROM users u
JOIN (
Select friend_id,user_id
from friends
where user_id IN (1,5)
) f ON f.friend_id = u.id
GROUP BY u.id,
u.image_width
ORDER BY u.id
You could use exists here to check if the corresponding IDs exist:
SELECT *,
case when exists (
select * from friends f
where f.friend_id = u.id and f.user_id = 5
) then 1 else 0 end amIfollowing
FROM users u
WHERE u.id IN (SELECT f.friend_id
FROM friends f
WHERE f.user_id = 1);
Example Fiddle
Looks like a JOIN will do, with distinct
SELECT distinct u.*, (f2.user_Id is not null) amIfollowing
FROM users u
JOIN friends f ON u.id = f.friend_id
LEFT JOIN friends f2 on f2.friend_id = f.friend_id and f2.user_id = 5
WHERE f.user_id = 1
ORDER BY u.id

SQL displaying data from one table based on id from another table

I am new at SQL I a need help in creating one.
I have 2 tables:
table 1:
table2:
And now I want first name and last name for all friends of one user example of user John Doe based on id from user and friend user id from table friends?
You should do 2 joins, one to get each friend's ID and another to retrieve that friend's name.
SELECT
U.ID,
U.first_name,
U.last_name,
N.first_name FriendFirstName,
N.last_name FriendLastName
FROM
[user] U
LEFT JOIN firends F ON U.ID = F.user_id
LEFT JOIN [user] N ON F.friend_id = N.id
Using a LEFT JOIN will make you see people with no friends (sniff).
If you want to see a particular user:
SELECT
U.ID,
U.first_name,
U.last_name,
N.first_name FriendFirstName,
N.last_name FriendLastName
FROM
[user] U
LEFT JOIN firends F ON U.ID = F.user_id
LEFT JOIN [user] N ON F.friend_id = N.id
WHERE
U.ID = 1948 -- Supplied ID
Do the self joins
select f.first_name, u.first_name as friend_name
from user u
join (
select u.first_name, f.friend_id
from friend f
join user u on u.id = f.user_id
) f on f.friend_id = u.id
A simple inner join will do
Select First_name,Last_name
from user inner join friends on user.id = friends.user_id
where friends.id = 1
Or you can replace with friends.user_id = 1 if you would like to look at user_id column
maybe I'm getting it wrong, but this is my suggeston:
SELECT
u1.first_name,
u1.last_name,
u2.first_name AS FriendFirstName,
u2.last_name AS FriendLastName,
FROM user AS u1
INNER JOIN friends AS f
ON t1.id = f.user_id
INNER JOIN user AS u2
ON f.friend_id = u2.id

Selecting all friends from mysql database

I got a database with with users and their connection as friends.
I just want to request simply all friends of a user with their username and the status of their friendship where status can be 0 for pending or 1 for accepted.
The Tables are:
users user_friend friend
- id - id -id
- username - friend_id
- user_id
- status
I just cant get the query right... Maybe someone can help me :)
What i want to get at the end is:
user_id username status
What i tried is: (even if its totally wrong x))
SELECT u.id, u.username, uf.friend_id, uf.status
FROM friends f
INNER JOIN user_friend uf ON uf.user_id = :user_id AND uf.friend_id = f.id
INNER JOIN users u ON u.id = uf.user_id
First you can DROP table friends.
Then friends for a particular user (id = 1234):
SELECT
CASE uf.user_id
WHEN 1234 THEN u.username
ELSE f.username END as friend_name,
CASE uf.user_id
WHEN 1234 THEN u.id
ELSE f.id END as friend_id,
uf.status
FROM
user_friends uf
JOIN
users f
ON uf.user_id = u.id AND uf.friend_id = 1234
JOIN
users u
ON uf.friend_id = f.id AND uf.user_id = 1234
Based on your data layout, you would seem to want something like this:
select f.*
from user u join
userfriends uf
on uf.user_id = u.id join
friend f
on uf.friend_id = f.id
where u.username = XXX;
Your data structure seems strange. Normally, there would be no friends table. Users would just befriend each other.
SELECT u.UserId, f.FriendId, uf.Status
FROM Friend f
INNER JOIN UserFriend uf
ON f.FriendId = uf.FriendId
INNER JOIN Users u
ON uf.UserId = u.UserId WHERE u.UserId = '1'
This gets all UserId, FriendId and Status from a certain UserId.

Making a sub-query on query results?

How do I unite these two?
This query:
SELECT f1.asked_user_id AS friend_id
FROM friends AS f1 JOIN friends AS f2
ON f1.asked_user_id = f2.asker_user_id
AND f1.asker_user_id = f2.asked_user_id
AND f1.asker_user_id = :user_id
WHERE f1.status = 1 AND f2.status = 1
With:
(SELECT GROUP_CONCAT(words_en.word) FROM connections JOIN words_en ON connections.word_id = words_en.id WHERE connections.user_id = friends.friend_id) As friend_words
And do yet another join:
JOIN users ON friends.friend_id = users.id
I've tried few things here: http://www.sqlfiddle.com/#!2/85d6d/36
First query selects two way friends from the table. I get friend_id from that query.
Now I want to know more about this friend_id.
So I want to use this id to do another query that would display all word_ids from connections table by that user, word_ids can be used to get actual words from words_en.
And lastly, I want to get users name_surname from users table.
Try this:
SELECT a.name_surname,GROUP_CONCAT(Distinct w.word Order by w.word asc) AS words
FROM (
SELECT f1.asked_user_id AS friend_id,
f1.created,
u.name_surname,
u.avatar
FROM friends AS f1
INNER JOIN friends AS f2 ON f1.asked_user_id = f2.asker_user_id
INNER JOIN users AS u ON f1.asked_user_id = u.id
AND f1.asker_user_id = f2.asked_user_id
AND f1.asker_user_id = 1
WHERE f1.status = 1 AND f2.status = 1
) a
LEFT JOIN connections c ON c.user_id = a.friend_id
LEFT JOIN words_en w ON c.word_id = w.id
GROUP BY 1;
sqlfiddle demo
Having your original query, what i did is treat that query as a table (a) and LEFT JOIN it with connections table. Then, LEFT JOIN the connections table with the words_en table to reach the desired words. This makes it possible to get all users that return from your original query, even when there are no connections/words.

SQL Statement that orders selected IDs first with condition

I have the following tables:
team_members
userid
teamid
users
uid
name_f
name_l
friends
friend_id
friend_one
friend_two
I use the following statement to select uid and profile_pic of the users that belong to a certain team.
SELECT DISTINCT u.uid, u.profile_pic
FROM friends f, users u, team_members m
WHERE m.teamid =$team_var
AND u.uid = m.userid
I also have to run the following to select the uid and profile_pic of the users who are friends with the logged-in user and belong to a certain team.
SELECT DISTINCT u.uid, u.profile_pic
FROM friends f, users u, team_members m
WHERE m.teamid =$team_var
AND u.uid = m.userid
AND m.userid = f.friend_two
AND f.friend_one =$session_id
I'm looking for a way to join these two and instead of running 2 queries, run one single query that can order and list the users who are friends with the logged-in user at the top. So let's say that a certain team has 30 users and 5 of those users are friends with the logged-in user, I would like to have the first 5 listed in the while loop following the statement to be those of the friends with the rest of the 25 randomly shown.
Thanks for your time.
This can easily be solved with an outer join. You will probably not be able to use an outer join without the explicit join syntax. Here:
SELECT
u.uid,
u.profile_pic,
(friend_id IS NOT NULL) AS is_friend
FROM team_members m
INNER JOIN users u ON m.userid = u.uid
LEFT JOIN friends f ON m.userid = f.friend_two AND f.friend_one = $session_id
WHERE m.teamid = $team_var
ORDER BY
is_friend DESC,
m.userid
The first two tables are joined using an inner join, so only the members of a specific team are returned (because we are filtering on teamid).
The friends table is outer-joined to the result of the previous join. More specifically, we a joining the subset of friends where friend_one is the current user. All rows from the previous result set are returned, but also rows from the friends's subset are returned where matched. Where not matched, the friends columns are filled with NULLs.
Using the NULL (or rather NOT NULL) test, we can see which team member is a friend and which isn't. The result of the test is returned as a column and also used as a sorting criterion for the output rows.
You would need to create another column which has a value of "friend" or not "friend". You would then use that column as an ORDER BY. The column you create could be a subquery to determine if the other user is a friend or not. The following code is wrong because you need to hook the data from the outer query into the subquery, but it should look something like:
SELECT DISTINCT u.uid, u.profile_pic,
EXISTS (SELECT DISTINCT u.uid, u.profile_pic
FROM friends f, users u, team_members m
WHERE m.teamid =$team_var
AND u.uid = m.userid
AND m.userid = f.friend_two
AND f.friend_one =$session_id
) AS myColumn
FROM friends f, users u, team_members m
WHERE m.teamid =$team_var
AND u.uid = m.userid
ORDER BY myColumn
I might suggest this:
select u.uid, u.profile_pic
from team_members t
join team_members m on t.teamid = m.teamid
join users u on m.userid = u.uid
left join friends f on t.userid = f.friend_one and m.userid = f.friend_two
where m.userid != t.userid
and t.userid = $session_id -- This line can be removed to view all (test)
order by
--t.teamid, t.userid, -- This line can be added to order all (test)
(case when f.friend_id is null then 1 else 0 end case),
f.friend_id, m.userid
I'm using explicit join syntax (which is normally recommended these days) and using that case statement in the order by to get friends to the top. I don't have MySQL running here to test it, but the syntax should be pretty standard (I'm running something very similar on SQL Server).