improving a friends list query : counting the mutual friends - php

i have two tables in my database one is to keep users info (users_table )
and the other one keeps track of the friends
users_table:
id username avatar
1 max max.jpg
2 jack jack.jpg
friends_table :
id u1_id u2_id
1 1 2
2 1 3
in every user profile i show his/her friends list
here is my query
select u.id,
u.username,
u.avatar
from friends_table f
join users_table u on f.u1_id = u.id || f.u2_id = u.id
where u.id <> $profile_id
and (f.u1_id = $profile_id || f.u2_id = $profile_id)
this query selects friends of the profile owner ($profile_id)
and join them with the user table to get each friend username and avatar
now i want to count the mutual friends between each friend and the profile owner is it possible to this in one query or should i do some long and probably slow query like this for each founded friend( it's just a example and it might have some syntax error ):
foreach ( $friends_list_query_resul as $qr ){
$friend_id = $qr['id'];
$mutual_count = mysql_query
( "select count(*) from friends_table where
($u1_id = $friend_id || $u2_id = $friend_id )
&&
( $u1_id IN ( SELECT `u1_id`,`u2_id` from friends_table where
($u1_id = $profile_id || $u2_id = $profile_id ) )
||
$u2_id IN ( SELECT `u1_id`,`u2_id` from friends_table where
($u1_id = $profile_id || $u2_id = $profile_id ) )
")
}

Your first query could also be written as:
select distinct u.id,
u.username,
u.avatar
from users_table u where u.id in
(select case when u1_id=$profile_id then u2_id else u1_id end
from friends_table f where case when u1_id=$profile_id
then u1_id else u2_id end =$profile_id);
The mutual friends query can be written as a single query in similar fashion:
select u.id, (select count(f.id) from friends f where
case when f.u1_id=u.id then u2_id else u1_id end in
(select distinct case when u1_id=$profile_id then u2_id else u1_id end
from friends where case when u1_id=$profile_id then u1_id else u2_id
end =$profile_id)
and u1_id=u.id or u2_id=u.id and
(u1_id <> $profile_id and u2_id <> $profile_id))
as mutual_frnds from user u where u.id <> $profile_id;
but you might want to performance test either of them before using.

All you need is one query:
select id, username, avatar, -- ...
(
select count(*)
from friends_table f1
inner join friends_table f2 on f1.u2_id = f2.u1_id and f2.u2_id = f1.u1_id
where f1.u1_id = users_table.id
)
as mutual_friend_count
from users_table
The meaning of the subquery is:
Give me the count of the "friend of a friend" relations the user participates in, such that the target of the first friend relation is the source of the second friend relation, and the target of the second friend relation is the source of the first one.

First, I do not understand why so complicated query to retrieve the user's friends... It should be simply achieved by this query:
select u.id,
u.username,
u.avatar
from friends_table f
left join users_table u on f.u2_id = u.id
where f.u1_id = $profile_id
Explain: the logged in user is the one whose id is the same as f.u1_id. Therefore we only select friends whose ids are in f.u2_id.
Then, to count the mutual friends of my friends we can use query like this:
select count(*) as mutual_count, f.u1_id as mutual_friend_id
from friends_table f
where f.u1_id IN (select f.u2_id from friends_table where f.u1_id = {$profile_id})
where $profile_id is the ID of logged in user...
Is this correct?

i've decided to add two rows for each friend relation to the table .
id u1_id u2_id
1 10 20
2 20 10
it makes the process easier and faster .

Related

group messages by same users

I have 2 tables .. 1- users,,2-messages
I wrote this query but it doesn't show the last subject and I need last subject
select (
CASE WHEN messages.sender = 68314 THEN messages.receiver ELSE messages.sender END
) AS user_id,
MAX(messages.added) last_added,messages.subject,
MAX(messages.id) as last_id,users.username
FROM messages
INNER JOIN users ON users.id = IF(messages.sender = 68314, messages.receiver, messages.sender)
WHERE (messages.sender = 68314 or messages.receiver = 68314) AND messages.sender!=0
GROUP BY
(
CASE WHEN messages.sender = 68314 THEN messages.receiver ELSE messages.sender END
)
ORDER BY last_added DESC
It shows first subject, not the last one.
please give this query a try. It first select max(id) of messages of people talking to id 68314 assuming those are created last...and joins back with messages, and the joined with users to get the other person's name.
SELECT last_messages.user_id,
m.added as last_added,
m.subject,
last_messages.last_id,
u.username
FROM messages m
INNER JOIN
(SELECT IF(sender = 68314,receiver,sender) as user_id,
MAX(id) as last_id
FROM messages
WHERE IF(sender = 68314,sender,receiver) = 68314
GROUP BY user_id
)last_messages
ON last_messages.last_id = m.id
INNER JOIN users u ON last_messages.user_id = u.id
ORDER by last_added;
sqlfiddle

How do I get friend list from Friends table with counts of friends of my friends

How do I get friend list from Friends table with counts of friends of my friends (Count not of my friends)
Friends table"
tbl_users_friends
Field 1: id
Field 2: user_id
Field 3: friend_user_id
and I need the out put as:
A has following friedns:
x (10)
y (2)
z (0)
Above is the list of my friends and in parenthesis contains their friends count.
Thanks
select user_id, count(*) cnt
from Friends
where user_id in
(select friend_user_id
from Friends
where user_id = user_id_of_A)
group by user_id
Try something like this:
select u.user_id, u.name, count(uf1.id) as num_friends
from tbl_users_friends uf
inner join tbl_users u on u.user_id = uf.friend_user_id
left join tbl_users_friends uf1 on uf1.user_id = uf.friend_user_id
where uf.user_id = 1
group by u.user_id, u.name
http://sqlfiddle.com/#!9/10033/1
You need to ajust the users table and column names.
Another solution with a subselect but without group by:
select u.user_id, u.name, (
select count(*)
from tbl_users_friends uf1
where uf1.user_id = uf.friend_user_id
) as num_friends
from tbl_users_friends uf
inner join tbl_users u on u.user_id = uf.friend_user_id
where uf.user_id = 1

Postgresql select people you may know orderded by number of mutual friends

I'm building a social network, and I want my members to be able to easily find new friends.
Just like in Facebook, I want to suggest them some people they may know by the number of mutual friends they have.
My PostgreSQL database structure for friendships is as following:
> PROFILES_FRIENDSHIPS
> ----------------------
> - fri_profile_from // int, Person who sent the friendship request
> - fri_profile_to // int, Person who received the friendship request
> - fri_accepted // tinyint, has the person accepted the friendship request?
This is the query I figured out in PostgreSQL for finding the number of mutual friends between 2 profiles (profile with ID 24, and profile with ID 26):
SELECT COUNT(*)
FROM profiles AS p
INNER JOIN (
SELECT (
CASE WHEN ( 26 = f.fri_profile_from ) THEN f.fri_profile_to
ELSE f.fri_profile_from END) AS fri_profile_from
FROM profiles_friendships AS f
WHERE 1 = 1
AND (f.fri_profile_to = 26 OR f.fri_profile_from = 26)
AND fri_accepted = 1)
AS f1
ON (f1.fri_profile_from = p.pro_id)
INNER JOIN (
SELECT (
CASE WHEN ( 24 = f.fri_profile_from ) THEN f.fri_profile_to
ELSE f.fri_profile_from END) AS fri_profile_from
FROM profiles_friendships AS f
WHERE 1 = 1
AND (f.fri_profile_to = 24 OR f.fri_profile_from = 24)
AND fri_accepted = 1)
AS f2
ON (f2.fri_profile_from = p.pro_id)
Now I have been trying to convert this query to make it find the profiles with the most mutual friends of me, but who are not friends with me. But without success... I also researched a lot of examples on this website, but most them are working with double records in the friendships table. Like if 24 is friends with 26, there are 2 records: (24, 26) and (26, 24). That makes them easier to join and to find mutual friends, but that's not how I want to build my database.
If someone could help me to get started on this query, I would be very grateful.
WITH friends AS(
SELECT p.pro_id, CASE WHEN f.fri_profile_from = p.pro_id THEN f.fri_profile_to
ELSE f.fri_profile_from END AS friend_id
FROM profiles
)
SELECT f2.pro_id, count(*) as friend_count
FROM friends AS f1
JOIN friends AS f2
ON f1.friend_id=f2.friend_id
AND f1.pro_id != f2.pro_id
AND f1.pro_id != f2.friend_id
WHERE f1.pro_id = :user_id
GROUP BY f2.pro_id
ORDER BY friend_count;
Step 1 Get everyone that isnt a friend
Select *
From profiles
where (from/to_friendship is not myID)
step 2 include a column with # of mutual friends and order by it
select *,
(select count(*) from [mutual friends query]) as NrOfMutualFriends)
From profiles
where (from/to_friendship is not myID)
Order by NrOfMutualFriends
Edit: Mutual Friends query:
Step 1 select all my friends and all his friends
select if(from = myId, to, from) as myfriendids
from PROFILES_FRIENDSHIPS where from = myid or to = myid
select if(from = hisId, to, from) as hisfriendids
from PROFILES_FRIENDSHIPS where from = hisId or to = hisId
Step 2 Combine these queries into 1
select count(*)
from
( select if(from = myId, to, from) as myfriendids
from PROFILES_FRIENDSHIPS where from = myid or to = myid) myfriends
inner join
( select if(from = hisId, to, from) as hisfriendids
from PROFILES_FRIENDSHIPS where from = hisId or to = hisId) hisfriends
on myfriendsids = hisfriendsids
you can easily create inline view in double records format:
with cte_friends(user_id, friend_id) as (
select
fri_profile_from, fri_profile_to
from PROFILES_FRIENDSHIPS
where fri_accepted = 1
union all -- or union if there could be duplicates
select
fri_profile_to, fri_profile_from
from PROFILES_FRIENDSHIPS
where fri_accepted = 1
)
select
f2.friend_id, count(distinct f2.user_id)
from cte_friends as f1
inner join cte_friends as f2 on f2.user_id = f1.friend_id
left outer join cte_friends as f3 on f3.user_id = f2.friend_id and f3.friend_id = f1.user_id
where
f1.user_id = 1 and f3.user_id is null and
f2.friend_id != 1
group by f2.friend_id
order by 2 desc
sql fiddle demo

php mysql join exists

I have an application, very basic description:
Users login -> they post "activities" (like snowboarding)
Users have friends
Users can bind multiple friends to activities
Users can "like" activities created by friends
What I need is a query to check if the user is allowed to "like" an activity by one of their friends. They are only allowed when one of the users friends was bound to the target activity.
users: id, name
usersFriends: id, uid, friendUid
activities: id, description
activitiesUsers: id, activityId, uid
activitiesLikes: id, activityId, uid
I hope someone can help me with this query, and if possible to return true or false. I hope my question is clear and thanks for your time :)
This should produce a list of activities.id for all of the user's friends.
SELECT
activities.id AS canLikeId
FROM
users u
JOIN usersFriends uf ON u.id = uf.uid
JOIN activitiesUsers au ON uf.friendId = au.uid
JOIN activities a ON a.id = au.activityId
Wrapped in an EXISTS, it looks like:
SELECT activities.id FROM activities aCanLike
WHERE EXISTS (
SELECT
a.id AS canLikeId
FROM
users u
JOIN usersFriends uf ON u.id = uf.uid
JOIN activitiesUsers au ON uf.friendId = au.uid
JOIN activities a ON a.id = au.activityId
WHERE a.id = aCanLike.id
)
Or something with an IN() clause that attempts to get everything from activities owned by any of the user's friends.
SELECT
activities.*
FROM activities JOIN activitiesUsers ON activities.id = activitiesUsers.activityId
WHERE activitiesUsers.uid IN (
SELECT friendUid FROM usersFriends WHERE uid = $userid
)
Assuming you know the user's id and activity id at the time of the query, you could do something like:
SELECT COUNT(*) FROM activitiesUsers WHERE activityId = 'xx' AND uid IN (SELECT friendUid FROM usersFriends WHERE uid = 'xx')
Should return 0 if none of their friends are bound to the activity or a positive number if they have friends bound to that activity ...
This should do it:
select
count(*) as CanLike -- 0 if false, >= 1 if true
from
usersFriends uf
join
activitiesUsers au
on
uf.friendUid = au.uid
where
au.id = $activityId
and
uf.uid = $userId
SELECT COUNT(*) AS allowed
FROM userFriends
JOIN activitiesUsers ON (userFriends.uid = activitiesUsers.uid
AND activitiesUsers.activityId = $activity_id)
WHERE userFriends.uid = $user_id

Friendship System Sql Structure & Query

I am coding a friendship system and it has two tables.
members
id
username
password
friends
id
user_id
friend_id
status
Let's say that i want a query that can select the friends IDs of the member $userId how possible to make this in one query?
I found a solution which is to make 2 queries. The fist selects the friends WHERE user_id = $userId AND the second selects friends WHERE friend_id = $userId and then MIX them in one array. If there is no other solution I'm going to use it.
please any ideas for both the SQL structure & Queries?
Use:
SELECT f.friend_id
FROM FRIENDS f
WHERE f.user_id = $user_id
UNION
SELECT t.user_id
FROM FRIENDS t
WHERE t.friend_id = $user_id
Using UNION will remove duplicates. UNION ALL would be faster, but it doesn't remove duplicates.
If you want to get the information for the friends from the MEMBERS table, use:
SELECT m.*
FROM MEMBERS m
JOIN (SELECT f.friend_id 'user_id'
FROM FRIENDS f
WHERE f.user_id = $user_id
UNION
SELECT t.user_id
FROM FRIENDS t
WHERE t.friend_id = $user_id) x ON x.user_id = m.id
BTW: I hope you're using mysql_escape_string on the variables, otherwise you risk SQL injection attacks:
You should be able to try using
SELECT m.*
FROM friends f INNER JOIN
members m ON f.friend_id = m.user_id
WHERE f.user_id = $userId
This will give you all the Friends details
To get BOTH have a look at
SELECT DISTINCT CASE WHEN f.user_id = $userId then f.friend_id else f.user_id END CASE
FROM friends f
WHERE f.user_id = $userId
OR f.friend_id = $userId
Why not inserting 2 rows for 1 friendship. For example:
Let's say we have 2 user will become friends
User_id : 1 &
Friend_id : 2
insert into friends (user_id, friend_id, status) values (1,2,0)
insert into friends (user_id, friend_id, status) values (2,1,0)
so you can select easily by simple select query.
Also it will ease the pain for your likely next question "How to find Mutual Friends".
Since you're asking for something simple like:
SELECT friend_id FROM friends WHERE user_id = id; [fill in the id]
I'll give you something fancier:
SELECT * FROM members AS m
WHERE m.id
IN (SELECT f.friend_id FROM friends AS f
WHERE f.user_id = (SELECT pm.id FROM members AS pm
WHERE pm.username = 'amindzx'));
Granted using a join over a sub-query would be better.
Also, there's no need for an id in the friends column, because only one relationship between a user_id and a friend_id should exist; these can both be described as the id columns in unison.

Categories