How to fix Sql query - mysql

Please i have been trying to get a query to return the friends a user has,in my members table, i have user id ,firstname and lastname of the member, i have user id column and friend id column in the friends table, now i am combining my members table with friends table so i can get the name of a friends.
users table
user_id firstname lastname
2 John drake
3 Hamer Joy
4 Finter Richy
friends table
friends_id user_id friend_id
1 2 3
2 4 2
3 4 3
here is the query i executed
SELECT a.friends_id,a.user_id,
a.friend_id, b.firstname, b.lastname
FROM friends AS a,users As b
WHERE (a.friend_id = b.user_id OR a.user_id = b.user_id) AND
(a.friend_id = 2 OR a.user_id =2)
here is the result i am getting
friends_id user_id friend_id firstname lastname
1 2 3 John drake
1 2 3 Hamer Joy
3 4 2 John drake
3 4 2 Finter Richy
This is the result i am expecting
friends_id user_id friend_id firstname lastname
1 2 3 Hamer Joy
3 4 2 Finter Richy

perhaps take a union of the specified individual's friends plus the users who have him as a friend:
select a.friends_id, a.user_id, a.friend_id, b.firstname, b.lastname
from friends a, users b
where a.user_id = 2 and b.user_id = a.friend_id
union
select a.friends_id, a.user_id, a.friend_id, b.firstname, b.lastname
from friends a, users b
where a.friend_id = 2 and b.user_id = a.user_id
This is all a single query resulting from the union of 2 inner queries.

I think the problem with your original query is that you are using an OR condition in your joins. Normally OR's in join conditions are not helpful.
Here is another solution that is a bit shorter and uses ANSI join syntax so you can clearly see the projections. Disclaimer, I haven't tested it in a database, so there may be an error. However, you can also access the original user name as well if you like (I've added that as the last two columns).
select F.friends_id, U.user_id, FR.user_id,
FR.firstname, FR.lastname,
U.firstname, U.lastname
FROM users U join Friends F on
U.user_id = F.user_id
JOIN Users FR on
F.friend_id = FR.user_id
where U.user_id = 2 or FR.user_id = 2
;
As a general rule, using the ANSI join syntax makes your query much easier to read and helps to ensure all of the join conditions are specified. It also gives you easy access to more join types.

Related

How to get name and surname from two id linked to same table SQL

I have i question about JOINign i have tables User and Category and Request
Id
name
surname
1
Alex
Morgan
2
Tom
Brady
3
Smith
Rowe
Category
Id
Category
1
Party
2
Football match
And now someone create request for another
Request
Id_request
Id_caller
Id_receiver
id_category
1
1
2
1
2
1
3
2
Now i want query to find all request where user 1 (Alex) is caller and get this result
Id_request
Caller_name
Caller_surname
Receiver_name
Receiver _surname
Category
1
Alex
Morgan
Tom
Brady
Party
2
Alex
Morgan
Smith
Rowe
Football match
I try with JOIN but i didn't get this result. Pls help.
You just need to join the tables and take the necessary columns.
However, you need to join the table user twice. When joining a table twice, at least one of them needs to have an alias. The query shown below uses aliases for all tables.
For example, you can do:
select
q.id_request,
c.name as caller_name,
c.surname as caller_surname,
r.name as receiver_name,
r.surname as receiver_surname,
c.category
from request q
join user c on c.id = q.id_caller
join user r on r.id = q.id_receiver -- joined user again with different alias
join category y on y.id = q.id_category
where q.id_caller = 1
You just need two joins, one for the caller and another for the receiver.
select
id_request,
uc.nm_user as "Caller_name",
uc.nm_surname as "Caller_surname",
rr.nm_user as "Receiver_name",
rr.nm_surname as "Receiver_surname",
c.nm_category
from
usr uc
join
request rc on rc.id_caller = uc.id_user
join
usr rr on rr.id_user = rc.id_receiver
join
category c on c.id_category = rc.id_category
where
uc.id_user = 1
Here is the complete example
https://www.db-fiddle.com/f/cA637bx33SFMeDtyUUNqsp/0
You need to join the User table twice with request table -
SELECT R.Id_request,
U1.Caller_name,
U1.Caller_surname,
U2.Receiver_name,
U2.Receiver_surname,
C.Category
FROM Request R
JOIN User U1 ON R.Id_caller = U1.Id
JOIN User U2 ON R.Id_receiver = U2.Id
JOIN Category C ON r.id_category = C.Id;

How to JOIN SELECT from multiple tables, where the SELECTS is based on different conditions?

I have three tables. One with notes Notes, one with users Users, and one a relational table between users and notes NotesUsers.
Users
user_id first_name last_name
1 John Smith
2 Jane Doe
Notes
note_id note_name owner_id
1 Math 1
2 Science 1
3 English 2
NoteUsers
user_id note_id
1 1
2 1
2 2
2 3
Hopefully, from the select statement you can tell what I'm trying to do. I am trying to select the notes that user_id = 2 has access to but doesn't necessarily own, but also along with this I'm trying to get the first and last name of the owner.
SELECT Notes.notes_id, note_name
FROM Notes, NotesUsers
WHERE NotesUsers.note_id = Notes.note_id AND NotesUsers.user_id = 2
JOIN SELECT first_name, last_name FROM Users, Notes WHERE Notes.owner_id = Users.user_id
My problem is that because the WHERE clause for first_name, and last_name versus that for notes are different, I don't know how to query the data. I understand that this is not how a JOIN works and
I don't necessarily want to use a JOIN, but I'm not sure how to structure the statement, so I left it in there so that you can understand what I'm trying to do.
You can join Notes with NoteUsers to check for access and with Users to add the user's details to the result:
SELECT n.noted_id, n.note_name, u.first_name, u.last_name
FROM Notes n
JOIN NoteUsers nu ON n.noted_id = nu.note_id AND nu.user_id = 2
JOIN Users u ON n.owner_id = u.user_id
you need here to use a query inside the main query. MySQL will return first all the note_id that the user with user_id = 2 has access to from NoteUser, then well build the outer query to return the first_name and the last_name of the owner.
SELECT u.first_name, u.last_name, n.note_name, n.note_id
FROM Notes AS n
LEFT JOIN Users AS u ON u.user_id = n.owner_id
WHERE n.note_id IN
(SELECT nu.note_id FROM NoteUser WHERE nu.user_id = 2)

Finding mutual friend in one way relationship table

want mysql query for finding mutual friend between two friend but
I am maintain the friendship of user in one way relationship for ex.
first is users table
id name
1 abc
2 xyz
3 pqr
Now second table is friend
id user_id friend_id
1 1 2
2 1 3
3 2 3
Now here i can say that abc(id=1) is friend of xyz(id=2) now similar way the xyz is friend of abc but now i want to find mutual friend between abc(id=1) and xyz(id=2) that is pqr so I want mysql query for that.
REVISED
This query will consider the "one way" relationship of a row in the friend table to be a "two way" relationship. That is, it will consider a friend relationship: ('abc','xyz') to be equivalent to the inverse relationship: ('xyz','abc'). (NOTE: we don't have any guarantee that both rows won't appear in the table, so we need to be careful about that. The UNION operator conveniently eliminates duplicates for us.)
This query should satisfy the specification:
SELECT mf.id
, mf.name
FROM (
SELECT fr.user_id AS user_id
, fr.friend_id AS friend_id
FROM friend fr
JOIN users fru
ON fru.id = fr.user_id
WHERE fru.name IN ('abc','xyz')
UNION
SELECT fl.friend_id AS user_id
, fl.user_id AS friend_id
FROM friend fl
JOIN users flf
ON flf.id = fl.friend_id
WHERE flf.user IN ('abc','xyz')
) f
JOIN users mf
ON mf.id = f.friend_id
GROUP BY mf.id, mf.name
HAVING COUNT(1) = 2
ORDER BY mf.id, mf.name
SQL Fiddle here http://sqlfiddle.com/#!2/b23a5/2
A more detailed explanation of how we arrive at this is given below. The original queries below assumed that a row in the friend table represented a "one way" relationship, in that "'abc' ff 'xyz'" did not imply "'xyz' ff 'abc'". But additional comments from the OP hinted that this was not the case.
If there is a unique constraint on friend(user_id,friend_id), then one way to get the result would be to get all of the friends of each user, and get a count of rows for that friend. If the count is 2, then we know a particular friend_id appears for both user 'abc' and for 'xyz'
SELECT mf.id
, mf.name
FROM friend f
JOIN users uu
ON uu.id = f.user_id
JOIN users mf
ON mf.id = f.friend_id
WHERE uu.name IN ('abc','xyz')
GROUP BY mf.id, mf.name
HAVING COUNT(1) = 2
ORDER BY mf.id, mf.name
(This approach can also be extended to find a mutual friend of three or more users, by including more users in the IN list, and changing the value we compare the COUNT(1) to.
This isn't the only query that will return the specified resultset; there are other ways to get it as well.
Another way to get an equivalent result:
SELECT u.id
, u.name
FROM ( SELECT f1.friend_id
FROM friend f1
JOIN users u1
ON u1.id = f1.user_id
WHERE u1.name = 'abc'
) t1
JOIN ( SELECT f2.friend_id
FROM friend f2
JOIN users u2
ON u2.id = f2.user_id
WHERE u2.name = 'xyz'
) t2
ON t2.friend_id = t1.friend_id
JOIN users u
ON u.id = t1.friend_id
ORDER BY u.id, u.name
NOTES
These queries do not check whether user 'abc' is a friend of 'xyz' (the two user names specified in the WHERE clause). It is only finding the common friend of both 'abc' and 'xyz'.
FOLLOWUP
The queries above satisfy the specified requirements, and all the examples and test cases provided in the question.
Now it sounds as if you want a row in that relationship table to be considered a "two way" relationship rather than just a "one way" relationship. It sounds like you want to want to consider the friend relationship ('abc','xyz') equivalent to ('xyz','abc').
To get that, then all that needs to be done is to have the query create the inverse rows,, and that makes it easier to query. We just need to be careful that if both those rows ('abc','xyz') and ('xyz','abc') already exist, that we don't create duplicates of them when we invert them.
To create the inverse rows, we can use a query like this. (It's simpler to look at this when we don't have the JOIN to the users table, and we use just the id value:
SELECT fr.user_id
, fr.friend_id
FROM friend fr
WHERE fr.user_id IN (1,2)
UNION
SELECT fl.friend_id AS user_id
, fl.user_id AS friend_id
FROM friend fl
WHERE fl.friend_id IN (1,2)
It's simpler if we don't include the predicates on the user_id and friend_id table, but that could be a very large (and expensive) rowset to materialize.
try this:
given that you want to get the mutual friends of friends 1 & 2
select friend_id into #tbl1 from users where user_id = 1
select friend_id into #tbl2 from users where friend_id = 2
select id, name from users where id in(select friend_id from #tbl1 f1, #tbl2 f2 where f1.friend_id=f2.friend_id)

MySQL Query to find friends and number of mutual friends

I have looked through the questions but I cant find anything that does exactly what I need and I can't figure out how to do it myself.
I have 2 tables, a user table and a friend link table. The user table is a table of all my users:
+---------+------------+---------+---------------+
| user_id | first_name | surname | email |
+---------+------------+---------+---------------+
1 joe bloggs joe#test.com
2 bill bloggs bill#test.com
3 john bloggs john#test.com
4 karl bloggs karl#test.com
My friend links table then shows all relationships between the users, for example:
+--------=+---------+-----------+--------+
| link_id | user_id | friend_id | status |
+---------+---------+-----------+--------+
1 1 3 a
2 3 1 a
3 4 3 a
4 3 4 a
5 2 3 a
6 3 2 a
As a note the a in the status column means approved, there could also be r(request) and d(declined).
What I want to do is have a query where if a user does a search it will bring back a list of users that they are currently not already friends with and how many mutual friends each user has with them.
I have managed to get a query for all users that are currently not friends with them. So if the user doing the search had the user id of 1:
SELECT u.user_id,u.first_name,u.surname
FROM users u
LEFT JOIN friend_links fl
ON u.user_id = fl.user_id AND 1 IN (fl.friend_id)
WHERE fl.friend_id IS NULL
AND u.user_id != 1
AND surname LIKE 'bloggs'
How then do I have a count of the number of mutual friends for each returned user?
EDIT:
Just as an edit as I don't think I am being particularly clear with my question.
The query that I currently have above will produce the following set of results:
+---------+------------+---------+
| user_id | first_name | surname |
+---------+------------+---------+
2 bill bloggs
4 karl bloggs
Those are the users matching the surname bloggs that are not currently friends with joe bloggs (user id 1).
Then I want to have how many mutual friends each of these users has with the user doing the search so the returned results would look like:
+---------+------------+---------+--------+
| user_id | first_name | surname | mutual |
+---------+------------+---------+--------+
2 bill bloggs 1
4 karl bloggs 1
Each of these returned users has 1 mutual friend as joe bloggs (user id 1) is friends with john bloggs and john bloggs is friends with both returned users.
I hope this is a bit more clear.
Thanks.
Mutual friends can be found by joining the friend_links table to itself on the friend_id field like so:
SELECT *
FROM friend_links f1 INNER JOIN friend_links f2
ON f1.friend_id = f2.friend_id
WHERE f1.user_id = $person1
AND f2.user_id = $person2
But bear in mind that this, in its worst case, is essentially squaring the number of rows in the friend_links table and can pretty easily jack up your server once you have a non-trivial number of rows. A better option would be to use 2 sub-queries for each user and then join the results of those.
SELECT *
FROM (
SELECT *
FROM friend_links
WHERE user_id = $person1
) p1 INNER JOIN (
SELECT *
FROM friend_links
WHERE user_id = $person1
) p2
ON p1.friend_id = p2.friend_id
Also, you can simplify your friend_links table by removing the surrogate key link_id and just making (user_id,friend_id) the primary key since they must be unique anyway.
Edit:
How would this be applied to the original query of searching for users that aren't already friends, I would like to do both in a single query if possible?
SELECT f2.user_id, COUNT(*) 'friends_in_common'
FROM friend_links f1 LEFT JOIN friend_links f2
ON f1.friend_id = f2.friend_id
WHERE f1.user_id = $person
GROUP BY f2.user_id
ORDER BY friends_in_common DESC
LIMIT $number
I am also thinking that the user_id constraints can be moved from the WHERE clause into the JOIN conditions to reduce the size of the data set created by the self-join and preclude the use of subqueries like in my second example.
This query lists anyone who's not friend with user 1 and whose surname matches '%bloggs%':
SELECT
users.user_id,
users.first_name,
users.surname,
Sum(IF(users.user_id = friend_links_1.friend_id, 1, 0)) As mutual
FROM
users inner join
(friend_links INNER JOIN friend_links friend_links_1
ON friend_links.friend_id = friend_links_1.user_id)
ON friend_links.user_id=1 AND users.user_id<>1
WHERE
users.surname LIKE '%bloggs%'
GROUP BY
users.user_id, users.first_name, users.surname
HAVING
Sum(IF(users.user_id = friend_links.friend_id, 1, 0))=0
just change the user id on the ON clause, and the surname on the WHERE clause. I think it should work correctly now!
If A is friend of B, then B is also a friend of A? Wouldn't it be better to use just a link instead of two links (and instead of two rows in friends_links)? Then you have to use two status columns, status1 and status2, and A is friend of B only if status1 = status2 = "a".
There are many ways to show mutual friends, e.g.:
SELECT friend_id
FROM friend_links
WHERE friend_links.user_id = $user1 or friend_links.user_id = $user2
AND NOT (friend_links.friend_id = $user1 or friend_links.friend_id = $user2)
GROUP BY friend_id
HAVING Count(*)>1
And this query shows for each user and anyone who's not his/her friend:
SELECT
users.user_id,
users.first_name,
users_1.user_id,
users_1.first_name
FROM
users INNER JOIN users users_1 ON users.user_id <> users_1.user_id
WHERE
NOT EXISTS (SELECT *
FROM friend_links
WHERE
friend_links.user_id = users.user_id
AND friend_links.friend_id = users_1.user_id)
(The only think I didn't check is the friendship status, but it's easy to add that check).
I'm still working on it, but it's not easy to combine nicely these two queries togheter. So this isn't exactly an answer, I'm just showing some ideas that i've tried.
But what do you need exactly? A query that returns every user with anyone who's not his/her friend and the number of friends in common, or is the user_id already given?
With some code it's not a problem to answer your question... but there has to be a nice way just by using SQL! :)
EDIT:
I'm still wondering if there's a better solution to this, in particular the next query could be extremely slow, but it looks like this might work:
SELECT
users_1.user_id,
users_2.user_id,
Sum(IF(users_1.user_id = friend_links.user_id AND users_2.user_id = friend_links_1.friend_id, 1, 0)) As CommonFriend
FROM
users users_1 INNER JOIN users users_2
ON users_1.user_id <> users_2.user_id,
(friend_links INNER JOIN friend_links friend_links_1
ON friend_links.friend_id = friend_links_1.user_id)
GROUP BY
users_1.user_id,
users_2.user_id
HAVING
Sum(IF(users_1.user_id = friend_links.user_id AND users_2.user_id = friend_links.friend_id, 1, 0))=0
(as before, i didn't check friendship status)
If user is given, you could put WHERE users_1.user_id=$user1 but it's better to just leave one user table, and filter the next INNER JOIN whith that user.

how to write conditional left join

I have 2 tables named user and userFriend. I want all user from user table, and specific member from userFriend table. Then I want to join both of them...
user
userID userName
1 aaa
2 bbb
3 ccc
4 ddd
5 eee
userFriend
userFriendID userID friendUserID
1 1 2
2 2 3
3 1 4
4 4 2
So if my userID = 1,
then I want result like
userID userName userFriendID friendUserID
2 bbb 1 2
3 ccc NULL NULL
4 ddd 3 4
5 eee NULL NULL
so in this way I want conditional for 2nd table, I only want to join 2nd table having userID = 1 with 1st table using left join.
It's an unconventional thing to ask for... but this gives you the results you want.
SELECT u.userID, u.userName, uf.userFriendID, uf.friendUserID
FROM user u
LEFT JOIN userFriend uf ON u.userID = uf.friendUserID AND uf.userID =1
WHERE u.userID !=1
For what you want you dont need a query from both tables. Just using userFriend will give you the data. You'll need the user table for names only.
SELECT DISTINCT userID FROM userFriend
WHERE friendUserID = ?
this will give you all users that have that specific friend that you need.
Optionally you can add a INNER JOIN to see what are the names:
SELECT DISTINCT f.userID, u.name
FROM userFriend f
INNER JOIN users u ON u.userID = f.userID
WHERE friendUserID = ?
UPDATE
A comment about your structure. You don't need the userFriendID. The combination of userID and friend ID can be your key.
UPDATE of the query
SELECT * FROM users u
LEFT JOIN userFriend f on u.userID = f.userID
WHERE f.friendUserID = ?
This will return you all users that have the friend X. The left join is not important in this query. This specific condition is making it irrelevant.
To get exactly what you want you need to execute this.
SELECT * FROM users u
LEFT JOIN (
SELECT DISTINCT * FROM userFriend f
WHERE friendID = 4
) x ON u.id = x.userId
It can be done using a subquery, but I dont think is the most logical way of organizing your tables.