I am trying to develop a function which shows me friends of friends, and how many mutual friends I have with those users. So far, I have been able to do the functions separately but not together.
Here is the main view I am using called userfriends. Where user1 is the first user and user2 is the friend.
This is the function I have developed to see mutual friends between two users
SELECT id FROM users
WHERE id IN (
SELECT user2 FROM userfriends WHERE user1 = me
) AND id IN (
SELECT user2 FROM userfriends WHERE user1 = second
)
Users is a main table which can link the user id found in the userfriends table to information about the user. Me and second are variables in the stored procedure which emulate the search for first and second users. This function works to plan.
The second function I have is to see all the users who are friends with my friends, but not with me.
SELECT user2 AS id
FROM userfriends
WHERE user1 IN (
#Selects my friends
SELECT user2 FROM userfriends
WHERE user1 = me
)
AND user2 <> me #Makes sure is not me
AND user2 NOT IN ( #Makes sure not already friend
SELECT user2 FROM userfriends
WHERE user1 = me
)
Again, all working to plan and me represents the user id. This will return a list of all my friends friends.
What I want to be able to get instead of a list of mutual users, or a list of my friends friends is:
A table which has my friends friend user ID and how many mutual friends me and that user shares. Etc: user: 1, friends_in_common: 103. If I'm not clear enough please ask and I will try and make it clearer. The two functions do it by themselves, but I'm just not sure how to merge it together.
-- use a self-join of userfriend*userfriend
-- to find all the friends that moi&toi have in common
-- , and count them.
-- (optionally) suppress the rows for (toi<=moi)
-- , because there are basically the same as the corresponding rows with
-- (toi>moi)
-- -----------------------------------
SELECT DISTINCT
uf1.user1 AS moi
, uf2.user1 AS toi
, COUNT(*) AS cnt
FROM userfriend uf1
JOIN userfriend uf2 ON uf1.user2 = uf2.user2
WHERE uf1.user1 < uf2.user1 -- Tie breaker, symmetry remover
GROUP BY uf1.user1, uf2.user1
;
Related
A user can have many interests.
An interest can be interested to many users.
My database looks like that:
Users table:
id - primary key,
name,
email,
Interests table:
id - primary key,
title
Users_To_Interests table:
id - primary key,
user_id(id from users table)
interest_id(id from interests table)
How can I improve Users_To_Interests table to be able to pick all users who have the same interest efficiently? user_id and interest_id columns don't have indexes or keys. If I need to add them, please show me how can I make that.
Edition 1: For example,
user1 has interests : interest1, interest2, interest3;
user2 has interests : interest3, interest4;
user3 has interests : interest3, interest5;
user4 has interests : interest4;
If I want to get all users who have interest1, I should receive user1;
If I want to get all users who have interest2, I should receive user1;
If I want to get all users who have interest3, I should receive user1, user2, user3;
The query to get users for interest #3 is very simple (use IN or EXISTS). With an index on users_to_interests(interest_id, user_id) this should be very fast.
select *
from users
where id in (select user_id from users_to_interests where interest_id = 3);
Here is a query which would find all users having interests 1 and 2. It should be clear how to generalize this to any number of interets. The subquery aggregates over users and finds those users who have the interests we want. We then join this back to the Users table to get the full information for each user.
SELECT
t1.*
FROM Users t1
INNER JOIN
(
SELECT ui.user_id
FROM Users_To_Interests ui
INNER JOIN Interests i
ON ui.interest_id = i.id
WHERE i.title IN ('interest2', 'interest3')
GROUP BY ui.user_id
HAVING COUNT(DISTINCT i.id) = 2
) t2
ON t1.id = t2.user_id;
my schema (MySQL, InnoDb):
Conversation
------------
id (int)
User
----
id (int)
UserConversation
----------------
user_id (int, FK)
conversation_id (int, FK)
Just mapping Users to Conversations (many-to-many).
And now, before I create new Conversations between user1 and user2 I want to check if conversation between exactly this two users already exist in DB.
So I want to select:
Conversation with User1, User2
but not:
Conversation with User1, User2, User3
Conversation with User1
Conversation with User1, User3
etc.
Other words: my goal is to prevent create duplicate conversations between group of users. How to achieve this is Yii2 ActiveRecord or pure SQL query?
You can use a series of exists / not exists criteria in the where clause to determine if a conversation is between particular users only. The only drawback is that for each user you need to add a new exists criteria:
select c.id
from conversation c
where exists (select 1 from userconversations uc where uc.conversation_id=c.id and uc.user_id=1)
and exists (select 1 from userconversations uc where uc.conversation_id=c.id and uc.user_id=2)
and not exists (select 1 from userconversations uc where uc.conversation_id=c.id and uc.user_id not in (1,2))
Another way would be to compare counts of records:
select conversation_id, count(if(user_id in (1,2),1,null)) as in_user, count(*) as all_user
from userconversations uc
group by conversation_id
having in_user=2 and all_user=2
This query would not require additional criteria in the where clause if you have more than 2 users in the list, but may be slower than the exists version because of the conditional counting. You need to test both solutions and see which works better for you.
Basically what i am trying to do is to suggest people based on common interests.
I have a table of Users(id, username, firstname, lastname, etc)
I have a table of Interested_People where UserID + Interested_in is stored.
I have a table of Contactlist where people who are added with each other is stored.(user1, user2, accepted [1,0])
What I want is to select * users table who are not my friend and they have same interest with me as well.
I searched a lot in internet but couldn't find something like so.
Here i do have created a query and it does exactly what I want. But it is very slow. Even it takes 16 to 20 second to output in PHPMyAdmin in my local machine. Now I Kindly request you guys if you can edit my query a bit and make it bandwidth & time efficient.
SELECT *
FROM users
WHERE id IN(SELECT userid
FROM interested_people
WHERE interested_in IN(SELECT interested_in
FROM interested_people
WHERE userid = [userid])
AND id NOT IN(SELECT user1 AS my_friends_userid
FROM contactlist f
WHERE f.user2 = [userid]
AND accepted = 1
UNION
SELECT user2 AS my_friends_userid
FROM contactlist f
WHERE f.user1 = [userid]
AND accepted = 1))
AND id != [userid]
ORDER BY Rand ()
LIMIT 0, 10;
[Userid] in this query is the ID of the user who is online. Like if i m online my ID will be 1.
This query suggest 10 random users who are not my friends and have same interests as me. But very slooow.
Thanks in advance!
Your problem suggests a self-join to get the users with common interests. Then, not exists to avoid the contact list. The following gets the list of users with common interests, ordered by the number of common interests:
select ip2.userid, count(*) as numInCommon
from interested_People ipme join
interested_People ip2
on ipme.interested_in = ip2.interested_in and
ipme.userid = $UserId and -- Your user id goes here
ip2.userid <> ipme.userid
where not exists (select 1
from contactlist cl
where cl.user1 = ipme.userid and cl.user2 = ip2.userid and
cl.accepted = 1
) and
not exists (select 1
from contactlist cl
where cl.user1 = ip2.userid and cl.user2 = ipme.userid and
cl.accepted = 1
)
group by ip2.userid;
For a social network site, I have a table with the ids of the inviter and the invitee and the status of the invitation ie accepted or not. According to what I've read, this seems to be best practice for a friends table.
Table friends
id | inviterid |inviteeid |status
however, for a given userid, I want to display all the "friends" the person has including their names. I can get a list of records of relationships with a simple query
Select * from friends WHERE inviterid = '33' or inviteeid = '33'"
But, this does not translate easily into a list of friends, since the friends could be either inviters or invitees so the field will be different depending on who invited whom.
Is there a better table design for a friends table? Alternatively, is there a sql syntax that would select friends who can be either inviters and invitees. Ultimately, I need to do a join to another table that has the users names.
You have a user table with below colums
userId|name|...
and your friend table
id | inviterid |inviteeid |status
if you want to find friends of a user that this user invited themes you can use below query!
select id, status from friend_tbl inner join user_tbl on user_tbl.id=friend_tbl.inviterid;
or if you want to get friend of this user that themes invitee his/her you can use below query:
select id, status from friend_tbl inner join user_tbl on user_tbl.id=friend_tbl.inviteeid;
I hope these can help you and i can understand your purpose!
Use union to get two kinds of friendS together, then you can join the results with username table.
Select inviterid as friend_id, status from friends where inviteeid = 33
Union
Select inviteeid as friend_id, status from friends where inviterid =33
I personally would just prefer to have two tables for this case, friend table and invite table
Friend Table
id | userid | friendid
So for selecting friends would be just
SELECT friendid FROM friend_table WHERE userid = 33
Invite Table
id | inviterid | inviteeid | status
For changing the status of invite table once the user approves, and insert new row to friend table
UPDATE invite_table SET status = 'accepted' WHERE inviterid = 33
INSERT INTO friend_table (`userid`, `friendid`) VALUES (33, $friendid)
I have a database table called users with a primary key of user_id for each user.
I also have a table called friends with two fields, user_id and friend_user_id.
The user_id field is always the lowest of the two user_id's in order to avoid duplicate entries.
Say I have two users in mind, (lets say user id 1 and user id 4 although they could be anything).
How would I return all rows from the users table for users that are friends with user 1 and user 4 (i.e mutual friends)?
I will give you the recipe:
Find all friends of user 1
Find all friends of user 2
Intersect them and the result will be the mutual friends.
Much like this:
UPDATE: Here's the query:
select f.friend_user_id from friends f where f.friend_user_id in (
select friend_user_id from friends where user_id=<id_of_user_a>)
and f.user_id=<id_of_user_b>
The ids returned by above query will be the id of all the users that are mutual friends of user_a and user_b. If you want to get all the details (name, etc) about those users, then do this:
select f.friend_user_id,u.* from friends f inner join users u
on u.user_id=f.friend_user_id
where f.friend_user_id in (
select friend_user_id from friends where user_id=<id_of_user_a>)
and f.user_id=<id_of_user_b>
SELECT friends.friend_user_id FROM user, friends
INNER JOIN friends ON friends.user_id = user.user_id
WHERE user.user_id = 1
AND friend.friend_user_id
IN (SELECT friends.friend_user_id
FROM user, friends
INNER JOIN friends ON friends.user_id = user.user_id
WHERE user_id = 4)