I am looking to show a mutual friends function but my user friends list is stored on another table.
I want to know if it's possible to use the data of each row as a clause for join B
SELECT * FROM ( SELECT * FROM `users`) a
CROSS JOIN (SELECT COUNT(*) AS MUTUAL_FRIENDS
FROM `user_friends` WHERE `friend_to` = `a`.`userid`) b
ORDER BY `b`.`MUTUAL_FRIENDS` ASC
What I am looking to do is use the userid from users table as a clause for the join with:
`friend_to` = `a`.`userid`
Expected results:
+--------=+---------+-----------+--------------+
| userid | Username |Photo_URL |MUTUAL_FRIENDS|
+---------+---------+-----------+--------------+
1 Somename1 /image1.png 3
4 Somename4 /image4.png 2
2 Somename2 /image2.png 1
3 Somename3 /image3.png 0
5 Somename5 /image5.png 0
Friends table:
+--------=+---------+-----------+
| friend_id | userid |friend_to |
+---------+---------+-----------+
1 1 2
2 2 1
3 1 3
4 3 1
5 1 4
6 4 1
Users Table
+--------=+---------+-----------+
| userid | Username |Photo_URL |
+---------+---------+-----------+
1 Somename1 /image1.png
2 Somename2 /image2.png
3 Somename3 /image3.png
4 Somename4 /image4.png
5 Somename5 /image5.png
Or am I doing this totaly wrong?
Here is one approach.
With CROSS JOIN
SELECT u.userid,u.Username,u.Photo_URL,IFNULL(Num,0) MUTUAL_FRIENDS
FROM Users u
LEFT JOIN (SELECT userid, COUNT(*) Num
FROM (SELECT a.userid
FROM Friends a
CROSS JOIN Friends b
WHERE a.userid = b.friend_to and a.friend_to = b.userid) r
GROUP BY userid) k ON u.userid = k.userid
ORDER BY MUTUAL_FRIENDS DESC, u.userid
Without CROSS JOIN
SELECT u.userid,u.Username,u.Photo_URL,IFNULL(Num,0) MUTUAL_FRIENDS
FROM Users u
LEFT JOIN (SELECT userid, COUNT(*) Num
FROM (SELECT a.userid
FROM Friends a, Friends b
WHERE a.friend_to = b.userid and b.friend_to = a.userid) r
GROUP BY userid) k ON u.userid = k.userid
ORDER BY MUTUAL_FRIENDS DESC, u.userid
I will try to explain little by little and please correct me if there is error.
The condition a.friend_to = b.userid and b.friend_to = a.userid is
used to select only mutual friend. Then if a user has this kind of
"mutual connection," the corresponding userid will appear once.
Get the count along with the userid.
Left join the userid and count table.
Result
userid Username Photo_URL MUTUAL_FRIENDS
1 Somename1 /image1.png 3
2 Somename2 /image2.png 1
3 Somename3 /image3.png 1
4 Somename4 /image4.png 1
5 Somename5 /image5.png 0
Given the data, user1 is mutual friend of user2, user3 and user4. User2 is only mutual friend of user 1 and so as user 3 and user4.
With CROSS JOIN SQL Fiddle
Without CROSS JOIN SQL Fiddle
Related
Users Table
id activated
--- ------------
1 1
2 1
3 0
4 1
5 1
6 1
Follow table
follower_id following_id
------------ --------------
1 2
2 1
4 2
5 1
3 2
5 2
6 1
3 4
6 2
3 1
4 1
Blocking table
blocking_id blocked_id
------------ --------------
2 4
Let's say I am the user with the id of 2
Expected Result
user_id mutual_followers
------------- -----------------
1 2
5 0
6 0
I want to get the mutual followers of users and exclude blocking or blocked and users whose activated is 0.
How can I get it done all the way by MySQL?
First use a CTE that returns all the followers of the user, filtered by your conditions to be active and non-blocked and then for each of the followers get their non-blocked followers.
Then filter the followers of the followers so that only the mutual followers are left (or none) and aggregate:
WITH cte AS (
SELECT f.follower_id user_id
FROM Follow f
INNER JOIN Users u ON u.id = f.follower_id AND u.activated
LEFT JOIN Blocking b ON b.blocking_id = f.following_id AND b.blocked_id = f.follower_id
WHERE f.following_id = 2 AND b.blocked_id IS NULL
)
SELECT c1.user_id,
COUNT(c2.user_id) mutual_followers
FROM cte c1
LEFT JOIN Follow f ON f.following_id = c1.user_id
LEFT JOIN Blocking b ON b.blocking_id = f.following_id AND b.blocked_id = f.follower_id
LEFT JOIN cte c2 ON c2.user_id = f.follower_id
WHERE b.blocked_id IS NULL
GROUP BY c1.user_id;
See the demo1 or demo2 (in case the 1st is inaccessible or sometimes does not run).
This is my table: (friends)
user_id
friend_id
1
2
1
3
1
4
2
1
2
5
3
7
3
8
4
1
4
5
9
2
10
1
11
8
11
7
The table contains 1 way relationship. For eg. User 1 is following User 3, but User 3 is not following User 1.
So Each user has Followers and Following data.
What I want in my output is:
Get Followers of selected user + followers and following of those Followers
Plus (+)
Get Followings of selected user + followers and following of those Followings
Desired Output:-
user_id
friend_id
1
2
1
3
1
4
2
1
2
5
3
7
3
8
4
1
4
5
9
2
10
1
This is my current query:
SELECT t1.*
FROM friends t1
JOIN friends t2 ON t1.friend_id=t2.user_id
WHERE t2.friend_id=$user_id
UNION
SELECT t1.*
FROM friends t1
JOIN friends t2 ON t1.friend_id=t2.friend_id
WHERE t2.friend_id=$user_id
;
I got this query from here: How to get friends of friends of friends... (#Adams answer)
But I don't think it is looping through all the condition that I want in my case. Any help would be much appreciated, thanks.
Edit:- #sajjad rezaei answer satisfies all the conditions, but is not exactly the output that I wanted.
(my bad, I should have added my desired output.)
Here is the dbfiddle link of the output:- https://www.db-fiddle.com/f/sWVYZKVWMbbEUkShJmje2Q/5
With this query:
SELECT CASE WHEN user_id = 1 THEN friend_id ELSE user_id END id
FROM friends
WHERE 1 IN (user_id, friend_id);
you get all the followers and followings of user = 1.
Join it to the table:
SELECT DISTINCT f.user_id, f.friend_id
FROM friends f
INNER JOIN (
SELECT CASE WHEN user_id = 1 THEN friend_id ELSE user_id END id
FROM friends
WHERE 1 IN (user_id, friend_id)
) t ON t.id IN (f.user_id, f.friend_id)
ORDER BY f.user_id, f.friend_id;
See the demo.
The fiddle
Here's one for MySQL 5.7 (8.0 or less):
SELECT user_id, friend_id
FROM (
SELECT f.user_id, f.friend_id FROM friends AS f WHERE 1 IN (f.user_id, f.friend_id) UNION
SELECT f.user_id, f.friend_id
FROM (
SELECT f.user_id, f.friend_id FROM friends AS f WHERE 1 IN (f.user_id, f.friend_id)
) AS f0
JOIN friends AS f
ON f0.user_id IN (f.friend_id, f.user_id)
OR f0.friend_id IN (f.friend_id, f.user_id)
) AS fof
ORDER BY user_id, friend_id
;
Here's one for MySQL 8.0+:
WITH RECURSIVE fof (user_id, friend_id, n) AS (
SELECT f.user_id, f.friend_id, 1 FROM friends AS f WHERE 1 IN (f.user_id, f.friend_id) UNION ALL
SELECT f.user_id, f.friend_id, n+1
FROM friends AS f
JOIN fof AS f2
ON ( f.user_id IN (f2.friend_id, f2.user_id)
OR f.friend_id IN (f2.friend_id, f2.user_id)
)
AND n < 2
)
SELECT DISTINCT user_id, friend_id
FROM fof
ORDER BY user_id, friend_id
;
The result:
user_id
friend_id
1
2
1
3
1
4
2
1
2
5
3
7
3
8
4
1
4
5
9
2
10
1
I used the following to count the larger data set...
Non-recursive:
SELECT COUNT(*)
FROM (
SELECT f.user_id, f.friend_id FROM friends AS f WHERE 1066 IN (f.user_id, f.friend_id) UNION
SELECT f.user_id, f.friend_id
FROM (
SELECT f.user_id, f.friend_id FROM friends AS f WHERE 1066 IN (f.user_id, f.friend_id)
) AS f0
JOIN friends AS f
ON f0.user_id IN (f.friend_id, f.user_id)
OR f0.friend_id IN (f.friend_id, f.user_id)
) AS fof
;
Recursive:
WITH RECURSIVE fof (user_id, friend_id, n) AS (
SELECT f.user_id, f.friend_id, 1 FROM friends AS f WHERE 1066 IN (f.user_id, f.friend_id) UNION ALL
SELECT f.user_id, f.friend_id, n+1
FROM friends AS f
JOIN fof AS f2
ON ( f.user_id IN (f2.friend_id, f2.user_id)
OR f.friend_id IN (f2.friend_id, f2.user_id)
)
AND n < 2
)
, xxx AS (
SELECT DISTINCT user_id, friend_id
FROM fof
ORDER BY user_id, friend_id
)
SELECT COUNT(*) FROM xxx
;
The result:
+----------+
| COUNT(*) |
+----------+
| 12125 |
+----------+
It might help you with your situation:
(SELECT f1.* ,
(SELECT GROUP_CONCAT(friend_id) from friends WHERE user_id = f1.friend_id) AS followerOFfollowers,
(SELECT GROUP_CONCAT(user_id) from friends WHERE friend_id = f1.friend_id) AS followingOFfollowers
FROM friends as f1
WHERE f1.user_id = 1 GROUP by f1.id , f1.friend_id)
UNION
(SELECT f1.* ,
(SELECT GROUP_CONCAT(friend_id) from friends WHERE user_id = f1.user_id) AS followerOFfollowers,
(SELECT GROUP_CONCAT(user_id) from friends WHERE friend_id = f1.user_id) AS followingOFfollowers
FROM friends as f1
WHERE f1.friend_id = 1 GROUP by f1.id , f1.user_id)
I have 3 tables:
tbl_user stores all user details (user_id,name,address)
user_id name address
1 a (shop) home
2 b (shop) bakerstreet
3 c (staff) wallstreet
4 d (staff) georgia
5 e (staff) sydney
tbl_user_group stores user type (user_id,user_type : 1=shop_owner,2=staff)
user_id user_type
1 1
2 1
3 2
4 2
5 2
tbl_user_association holds the shop_owner and staff relation (shop_owner_id, staff_id)
shop_owner_id staff_id
1 3
1 4
1 5
2 3
2 4
desired result
i want to display the list of staffs and the respective shops that they are associated with as follows:
user_id staff_name shop_owner
3 c a,b
4 d a,b
5 e a
I tried using the group_concat as mentioned here. The query is as follows:
SELECT
u.id AS user_id,
u.name AS staff_name,
(SELECT GROUP_CONCAT(u.name separator ',') FROM tbl_user u WHERE u.id = ua.shop_owner_id) AS
shop_owner
FROM tbl_user u
JOIN tbl_user_group ug ON u.id = ug.user_id
LEFT JOIN tbl_user_association ua ON u.id = ua.staff_id
WHERE ug.user_type = 2
GROUP BY u.id
But it returns single row of staffs as below. where have i gone wrong?
user_id staff_name shop_owner
3 c a
4 d a
5 e a
This is how I'd do it:
SELECT
u.user_id,
u.name,
GROUP_CONCAT( so.name )
FROM
tbl_user_group ug
INNER JOIN tbl_user u
ON ( ug.user_id = u.user_id )
INNER JOIN tbl_user_association ua
ON ( ua.staff_id = u.user_id )
INNER JOIN tbl_user so -- here join again the user table to get shop owners names
ON ( ua.shop_owner_id = so.user_id )
WHERE
ug.user_type = 2
GROUP BY
u.user_id;
I want to make a selection that look up values of ID to Username from two columns in the same table:
Table: Gift
FromID ToID
1 2
2 6
3 2
5 3
TableL Users
UserID Username
1 A
2 B
3 C
4 D
5 E
6 F
Expected Output:
FromUser ToUser
A B
B F
C B
E C
Here is one trick using JOIN with IN condition
SELECT Max(CASE WHEN FromID = u.UserID THEN Username END),
Max(CASE WHEN ToID = u.UserID THEN Username END)
FROM users u
JOIN gift g
ON u.UserID IN ( g.FromID, g.ToID )
GROUP BY FromID,
ToID
Or you need to join the users table twice
SELECT f.Username,
t.Username
FROM gift g
LEFT JOIN users f
ON f.UserID = g.FromID
LEFT JOIN users t
ON t.UserID = g.ToID
Looks like you need a recap on how to work with several relationships between the same two tables.
Your gift table has a relationship that describes who gave the gift - I call it giver here; and a relationship that described who received the gift - I call it receiver here. Both relationships from gift are many-to-one towards the users table.
You simply join the same table twice and give that table a good correlation name - giver and receiver in my example - to show in the code what you're using them for.
Like so:
WITH
gift(fromid,toid) AS (
SELECT 1,2
UNION ALL SELECT 2,6
UNION ALL SELECT 3,2
UNION ALL SELECT 5,3
)
,
users(userid,username) AS (
SELECT 1,'A'
UNION ALL SELECT 2,'B'
UNION ALL SELECT 3,'C'
UNION ALL SELECT 4,'D'
UNION ALL SELECT 5,'E'
UNION ALL SELECT 6,'F'
)
SELECT
giver.username as fromUser
, receiver.username as toUser
FROM gift
JOIN users AS giver
ON gift.fromid=giver.userid
JOIN users AS receiver
ON gift.toid =receiver.userid
ORDER BY 1,2
;
fromUser|toUser
A |B
B |F
C |B
E |C
I have 3 tables as follows in my database. this is used to a application just like foursqure. i need help with the problem of writing the sql statement i have asked in the bottom of this.
thank you
user_details
user_id | fname
----------------
1 | Losh
8 | Dush
9 | Rosh
10 | NELL
friends
user_idf |user_idff
----------------
1 | 8
8 | 9
10 | 1
Check_in
check_in_id |user_id | place | date
--------------------------------------------
1 | 8 | Hotel | 01/01/2012
2 | 9 | Home | 05/01/2012
3 | 1 | Junction | 08/01/2012
4 | 1 | Rest | 11/01/2012
5 | 9 | Hotel | 15/01/2012
6 | 8 | Home | 15/01/2012
i get the user's who are friends with 8 and user 8 details AND the check in places as follows
SELECT a.`user_id`, a.`fname` , b.*
FROM `user_details` a, `check_in` b
WHERE (b.user_id = 8
OR b.user_id in (select user_idf from friends where user_idff = '8' union select user_idff from friends where user_idf = '8')) AND b.user_id = a.user_id
how do i write the sql to select who are friends with 8 and user 8 details AND the last check in place of those users
explanation::
i seeks for a answer such as
user id name place date
1 LOSH Rest 11/01/2012
8 DUSH HOME 15/01/2012
9 ROSH HOTEL 15/01/2012
Join it to the table returned by:
(SELECT `user_id`, `place` FROM Check_in GROUP BY user_id ORDER BY `date` DESC)
That should give you one entry per user, and since it's sorted in reverse by date, that entry should be the most recent.
But when i group by it gives me the first dates not the latest date
How about this:
(SELECT user_id, place
FROM (SELECT * FROM Check_in ORDER BY `date` DESC) tmp
GROUP BY user_id)
SELECT user_id, fname, c.place
FROM user_details u
INNER JOIN (SELECT IF(user_idff = 8, user_idf, user_idff) AS user_id
FROM friends
WHERE (user_idff = 8 OR user_idf = 8)
) f
ON u.user_id = f.user_id
LEFT JOIN (SELECT c1.user_id, place
FROM Check_in c1
LEFT JOIN Check_in c2
ON c1.user_id = c2.user_id AND
c1.date < c2.date
WHERE c2.date IS NULL
) c
ON u.user_id = c.user_id;
This doesn't break ties but it's a straighforward way of answering your question.
EDIT
I just re-read you question and I see that you want user 8 info also. It's not clear whether you want user 8 as a separate row or with info in line with the friends' rows.
select *
from
friends as f inner join check_in as ci on ci.user_id = f.user_idff
inner join user_details as ud on ud.user_id = f.user_idff
inner join user_details as ud8 on ud8.user_id = f.user_idf
where
f.user_idf = 8
and date = (
select max(date)
from friends as f2 inner join check_in as ci on ci.user_id = f2.user_idff
where f2.user_idf = f.user_idf
)
EDIT 2
You request may be a small bit unclear about determining which check-in location to return. Use this option if you want the latest location of each friend individually. The first query finds the most recent location among all friends. Obviously these are two variations on an identical theme.
select *
from
friends as f inner join check_in as ci on ci.user_id = f.user_idff
inner join user_details as ud on ud.user_id = f.user_idff
inner join user_details as ud8 on ud8.user_id = f.user_idf
where
f.user_idf = 8
and date = (
select max(date)
from check_in as ci
where ci.user_id = f.user_idff
)
(SELECT a.user_id, a.place, b.fname, a.date, a.time, a.check_in_id
FROM (SELECT * FROM check_in ORDER BY date DESC) as a, user_details as b
WHERE a.user_id = b.user_id AND (a.user_id in (select user_idf from friends where user_idff = '8' union select user_idff from friends where user_idf = '8') OR a.user_id = 8)
GROUP BY a.user_id)
above query gave me the required answer.
thank you all for the help given