MySQL query that selects non-friends of a user - mysql

I have the following schema of a Mysql database:
User(Id, FirstName, LastName, NickName, Etc.)
Request(SenderId, ReceiverId)
Friendship(Id1, Id2)
I consider friendship to be an undirected relation, which means that for every friendship, I insert it twice to the Friendship table. (Let me know if this is not a good idea, please).
What I am trying to retrieve is a list of users, who are not friends to a specific user (let me name him UserX), nor have a current request ongoing to/from him.
My initial trials led me to this:
SELECT User.Id, User.NickName, User.Picture FROM User
LEFT JOIN Friendship A ON User.Id = A.Id1
LEFT JOIN Friendship B ON User.Id = B.Id2
LEFT JOIN Request C ON User.Id = C.Sender
LEFT JOIN Request D ON User.Id = D.Reciever
WHERE User.Id <> ?
And, of course the placeholder is UserX's Id.
This doesn't work because, although the tuples that has friendships or requests with UserX are eliminated, The friends still appear because they have friendships with other users!
Thanks in advance.

If you want an efficient solution, use not exists multiple times:
select u.*
from user u
where not exists (select 1 from friendship f where f.id1 = u.id and f.id2 = ?) and
not exists (select 1 from friendship f where f.id2 = u.id and f.id1 = ?) and
not exists (select 1 from request r where r.SenderId = u.id and r.ReceiverId = ?) and
not exists (select 1 from request r where r.ReceiverId = u.id and r.SenderId = ?);
In particular, this can take advantage of indexes on:
friendship(id1, id2)
friendship(id2, id1)
request(SenderId, ReceiverId)
request(ReceiverId, SenderId)
This should have much better performance than solutions that union subqueries together.

Using a left join to a union list:
select *
from User u1
left join
(
select ID2 as id
from Friendships
where ID1 = 'UserX'
union all
select ID1
from Friendships
where ID2 = 'UserX'
union all
select Sender
from Request
where Receiver = 'UserX'
union all
select Receiver
from Request
where Sender = 'UserX'
) ux
on ux.id = u1.id
where ux.id is null
and ux.id <> 'UserX'

What if you collect all distinct IDs from table "request" and "Friendship" and then select records from Users ID not available in the above list.
SELECT Id, FirstName, LastName, NickName
FROM User
WHERE ID NOT IN
(
SELECT DSTINCT Id1 ID FROM Friendship
UNION
SELECT DSTINCT Id2 FROM Friendship
UNION
SELECT DSTINCT SenderId FROM Request
UNION
SELECT DSTINCT ReceiverId FROM Request
)A

Related

How to get data from 3 tables with same user id?

I have 3 tables like so
Table 1: UserInfo
user_id userName
123 userOne
Table 2: Post
user_id postContent
123 This is test message
Table 3: LikePost
user_id likesPostId
123 This is test message
I would like to run a query to get total number of post likes, posts, and user information from those 3 tables.
I can do this for each one such as in Post table:
SELECT COUNT(*) FROM Post WHERE Post.user_id = '123'
and SELECT * FROM UserInfo WHERE UserInfo.user_id = '123'
Is anyone have better solution in just 1 query? Thank you so much!
Use a structured query (with subqueries) something like this.
SELECT u.user_id, u.userName, p.num postcount, l.num likecount
FROM UserInfo u
LEFT JOIN (
SELECT COUNT(*) num,
user_id
FROM Post
GROUP BY user_id
) p ON u.user_id = p.user_id
LEFT JOIN (
SELECT COUNT(*) num,
user_id
FROM LikePost
GROUP BY user_id
) l ON u.user_id = l.user_id
What's going on here? The two subqueries, for example
SELECT COUNT(*) num,
user_id
FROM LikePost
GROUP BY user_id
each generate a virtual table with either zero or one row per user_id, showing a count for each user_id. You then join those virtual tables to your UserInfo table.
Use LEFT JOIN because ordinary innner JOIN will suppress users that lack either posts or likes.
Try This
SELECT ui.userName,Count(p.*),
Count(lp.*) as TotalPostLikes
FROM UserInfo ui
INNER JOIN Post p on p.user_id=ui.user_id
INNER JOIN LikePost lp on lp.user_id=ui.user_id
WHERE ui.user_id = '123'
GROUP BY ui.userName
If you want to select Username, Post and Likes on post, try the following
SELECT ui.userName,p.postContent as PostContent,
(SELECT COUNT(lp.user_id) FROM LikePost lp
WHERE lp.user_id=ui.user_id) as Likes,
(SELECT COUNT(_p .user_id) FROM Post _p
WHERE _p .user_id=ui.user_id) as TotalPosts
FROM UserInfo ui
INNER JOIN Post p on p.user_id=ui.user_id
WHERE ui.user_id = '123'
Yes you can do it within one query using leftjoin on Post and LikePost like below
SELECT COUNT(*),User.userName FROM UserInfo as User
leftjoin Post as Post on Post.user_id = User.user_id
leftjoin LikePost as LikePost on LikePost.user_id = User.user_id
where Post.user_id = 123
group by Post.user_id

Convert Sub-query into Joins

I am trying to convert below query via Joins,
SELECT DISTINCT req.*
FROM request req
WHERE (req.user_id IN (
SELECT id from user where id in (SELECT user_id FROM team_member team WHERE team.team_id IN ('344', '723')) and user.active = 1
)
OR req.user_id IN (
SELECT id from user where id in (SELECT approved_employee_id from approver where approver_id = '269') and user.active = 1
))
AND req.status = 'pending';
returns 124 records.
I have wrote below query via joins but doesn't work,
SELECT DISTINCT req.*
FROM request req
LEFT JOIN user u ON u.id = req.user_id AND u.active = 1
LEFT JOIN team_member team ON team.user_id = u.id AND team.team_id IN ('344', '723')
LEFT JOIN approver app ON app.approver_id = u.id AND app.approver_id = '269'
AND req.status = 'pending';
returns more than 500 records.
two issue with this joins, it includes those requests too where status is not pending.
Below is the schema
I have below tables
tbl_approver =
id,
approver_id (FK tbl_user),
approver_team_id (FK tbl_team),
approved_employee_id (FK tbl_user)
tbl_team_mambers =
id,
team_id (FK),
user_id (FK)
tble_user =
id,
email,
username
active
tbl_request =
id,
user_id,
status
Teams Members : A user is a member of multiple teams.
Approver :
For each team there is an approver, who is also the member of that team.
there can be direct approver of an employee regardless of team.
request : inventory requests by the user
Note : as an approver, i can be member of many teams but only approver
of some team. or some employee too.
Query wanted : as an approver i want all request of my teams whom i am approver.
For correct results of join's you need to any joined "table" return only one row for user_id. And do not use left if needed only rows existing in all tables.
SELECT req.*
FROM request req
JOIN user ON user.id=req.user_id
JOIN (
SELECT distinct approved_employee_id as user_id from approver where approver_id = '269'
UNION
SELECT distinct user_id FROM team_member team WHERE team.team_id IN ('344', '723')
) A ON req.user_id=A.user_id
where user.active = 1
AND req.status = 'pending';
Or, if needed only "real table":
SELECT DISTINCT req.*
FROM request req
JOIN user u ON u.id = req.user_id AND u.active = 1
LEFT JOIN team_member team ON team.user_id = u.id AND team.team_id IN ('344', '723')
LEFT JOIN approver app ON app.approver_id = u.id AND app.approver_id = '269'
WHERE req.status = 'pending'
AND (team.user_id is not null OR app.approver_id is not null)
Watch the LEFT JOIN syntax, here's the visual expression of joins
SELECT DISTINCT req.*
FROM request req
LEFT JOIN user u ON u.id = req.user_id AND u.active = 1
...
This will select all distinct records from request, and JOIN them to user. The join_condition after ON will only affect the join relation.
If there is no matching row for the right table in the ON or USING part in a LEFT JOIN, a row with all columns set to NULL is used for the right table.
Join syntax from Mysql Documentation
If you really want to use LEFT JOIN to filter all record from request, consider moving the condition_expr to WHERE
SELECT DISTINCT req.*
FROM request req
LEFT JOIN user u ON u.id = req.user_id
...
WHERE u.active = 1
AND ...
If you only need request that satisfies the join condition to user, you should consider use Join rather than LEFT JOIN.

What would the best query be for selecting values from two separate database tables?

I'm trying to output something based on these database values, there are two tables. users and transactions. users has the following fields:
id, username, firstname, lastname, usertype, points
The second table, transactions has the following:
id, sender_id, receiver_id, points
The sender_ID is the ID of the person who sent the points, receiver_id is the ID of the person receiving, and so on.
How can I query it to output values from both tables in php, so it would display like this:
John Doe has sent Jane Doe 3 points.
Can you try it?
In MYSQL
SELECT CONCAT(u1.firstname,' ',u1.lastname) as sender,CONCAT(u2.firstname,' ',u2.lastname) as receiver,t.points as points
FROM transactions t
INNER JOIN users u1 ON u1.id=t.sender_id
INNER JOIN users u2 ON u2.id=t.receiver_id
In PHP
You retrieve
echo $row['sender'].' has sent '.$row['receiver'].$row['points'].' points';
Just use two joins to the same table:
SELECT u1.firstname, u2.firstname, t.points FROM transactions t
INNER JOIN user u1 ON u1.id = t.sender_id
INNER JOIN user u2 ON u2.id = t.receiver_id
WHERE t.id = 1
Use Joins...
SELECT `t1`.`username`, `t2`.`username`, `t`.`points` FROM transactions `t`
INNER JOIN `table` `t1` ON `t1`.`id` = `t`.`sender_id`
INNER JOIN `table` `t2` ON `t2`.`id` = `t`.`receiver_id`
WHERE `t`.`id` = 1
Hello you can make a LEFT JOIN (eg: http://www.tutorialspoint.com/mysql/mysql-using-joins.htm) or you can use this:
SELECT a.username, a.firstname, a.lasname, b.points, b.receiver_id
FROM table1 a, table2 b
WHERE table1.id = table2.sender_id;

Mysql database selecting with relation

I have this table that I hold userlist for my msn application. There's another table for the friendship where it has two foreign keys from userlist.
user: id, name, online, ip...
friend: id1, id2
I want the information of the users that are friend with a specific id.
I'm using this sql query:
SELECT (latest_ip, email, online, pass, status)
from im.user JOIN im.friend ON user.id = friend.id1
WHERE user.id = 5
what am i missing?
remove the parenthesis on your select clause, you don't need them
SELECT latest_ip, email, online, pass, status
from im.user
INNER JOIN im.friend
ON user.id = friend.id1
WHERE user.id = 5
UPDATE 1
you need to have extra join with the table user again since you want to get the informations of the user's friends.
SELECT c.*
from im.user a
INNER JOIN im.friend b
ON a.id = b.id1
INNER JOIN im.user c
ON b.id2 = c.id
WHERE a.id = 5

SQL - Join two tables

I have two tables
Users: (id, name)
Relations: (user_id, relation_id)
User_id and relation_id are both ids from the table users.
What I want is ro recover all users who are friend with a specific user.
And here is my sql command: that doesn't work:
SELECT *
FROM users
NATURAL JOIN relations
WHERE user_id IN (SELECT id FROM users WHERE name='John doe');
Could you help me?
SELECT users.*
FROM relations
INNER JOIN users
ON relations.relation_id = users.id
WHERE relations.user_id = 12345;
You can also get the id with a subquery, just as you did in your example:
WHERE relations.user_id IN (SELECT id FROM users WHERE name='John doe');
To get all the relations a person has , the following query will work..
SELECT * FROM users us JOIN relations re ON us.id = re.relation_id
WHERE re.user_id = (
SELECT id
FROM users
WHERE name = 'John doe'
)
Try this
SELECT * FROM users as a
JOIN relations as b on a.id = b.user_id
WHERE b.user_id IN (SELECT id FROM users WHERE name='John doe')
SELECT user_id
FROM relations re
JOIN users us
ON us.id = re.user_id
WHERE relation_id = (
SELECT id
FROM users
WHERE name = 'John Doe'
)
Side note: you can't use NATURAL JOIN here, 'cause there is no column that have the same name and type in the two tables.
isnt this just a matter of querying the relations tables by the userId you are looking for?
select *
from relations
where user_id = #IdYouArelookingFor or relation_id = #IdYouArelookingFor
SELECT friend.*
FROM users AS friend
JOIN relations AS r
ON r.relation_id = friend.id
JOIN users AS u
ON u.id = r.user_id
WHERE u.name = 'John doe' ;
Friends are people with the same relation_id?
SELECT a.name FROM users a JOIN relations b on a.id = b.user_id
WHERE b.relation_id in
(select relation_id from relations where id = 'userid of user you are looking for')
AND a.id != 'userid of user you are looking for'
If your logic is different pls tell how it is working