MySQL query. Can't manage to get it - mysql

I have two tables inside a database. One stores unique userNames and a unique id and the other stores which users from the previous table are "friends": ex:
table users:
id | username
---------------
100 | aaa
200 | bbb
300 | ccc
table friends:
id | user | friend
-------------------
1 | 100 | 200
2 | 300 | 100
3 | 300 | 200
Like in the above example, user 100 is friends with 300 and also 200.
I'd like to display a list containing all of users 100 friends. Keep in mind he can appear in the "friends" table on both columns (user and friend). Can't figure out how the query should look like. Everything i try, it duplicates rows and whatnot.
I know it's trivial, but i'm new at this.

This will work with no duplicate
SELECT distinct id FROM(
SELECT friend as id FROM friends
WHERE user = 100
UNION ALL
SELECT user as id FROM friends
WHERE friend = 100) ;

Try this:
Select distinct u.id, u.username
from Users u
inner join Friends f on u.id = f.id
where f.friend = 100
UNION ALL
Select distinct u.id, u.username
from Users u
inner join Friends f on u.id = f.id
where f.user = 100
Or:
Select distinct u.id, u.username
from Users u
inner join Friends f on u.id = f.id
where f.user = 100 or f.friend = 100

try below :
Select distinct u.id, u.username
from Users as u
left join Friends as f on (u.id = f.user or u.id = f.friend)
where u.id=100

Related

MySQL how get users who are not my friends

could someone help me, I need to get users who are not my friends
I have the following tables
users
id
user
1
Name1
2
name2
and the table friends:
id
id_user
id_friends
1
2
3
2
5
4
I try something like that, but in some queries it returns the ones that I already have added
Select u.*
from users u left join friends f
on f.id_user = 2
and f.id_friends = u.id
where u.id <> 2
and f.id_friend is null
One way is using NOT EXISTS to return all users not already "friended". That includes users not already in the friends table.
SELECT u.*
FROM users u
WHERE u.id <> 2
AND NOT EXISTS (
SELECT NULL
FROM friends f
WHERE ( f.id_friend = u.id AND f.id_user = 2 )
OR ( f.id_friend = 2 AND f.id_user = u.id )
)
Results:
id | user
-: | :----
1 | Name1
db<>fiddle here

MySQL where joined value is multiple ANDs

Running into a seemingly simple JOIN problems here..
I have two tables, users and courses
| users.id | users.name |
| 1 | Joe |
| 2 | Mary |
| 3 | Mark |
| courses.id | courses.name |
| 1 | History |
| 2 | Math |
| 3 | Science |
| 4 | English |
and another table that joins the two:
| users_id | courses_id |
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
I'm trying to find distinct user names who are in course 1 and course 2
It's possible a user is in other courses, too, but I only care that they're in 1 and 2 at a minimum
SELECT DISTINCT(users.name)
FROM users_courses
LEFT JOIN users ON users_courses.users_id = users.id
LEFT JOIN courses ON users_courses.courses_id = courses.id
WHERE courses.name = "History" AND courses.name = "Math"
AND courses.name NOT IN ("English")
I understand why this is returning an empty set (since no single joined row has History and Math - it only has one value per row.
How can I structure the query so that it returns "Joe" because he is in both courses?
Update - I'm hoping to avoid hard-coding the expected total count of courses for a given user, since they might be in other courses my search does not care about.
Join users to a query that returns the user ids that are in both courses:
select u.name
from users u
inner join (
select users_id
from users_courses
where courses_id in (1, 2)
group by users_id
having count(distinct courses_id) = 2
) c on c.users_id = u.id
You can omit distinct from the condition:
count(distinct courses_id) = 2
if there are no duplicates in users_courses.
See the demo.
If you want to search by course names and not ids:
select u.name
from users u
inner join (
select uc.users_id
from users_courses uc inner join courses c
on c.id = uc.courses_id
where c.name in ('History', 'Math')
group by uc.users_id
having count(distinct c.id) = 2
) c on c.users_id = u.id
See the demo.
Results:
| name |
| ---- |
| Joe |
You can use in operator and use select to generate list of potential users_id attending the second course, to find matching ones in the first course. This is many times faster than using joins.
select distinct u.users_id, users.name
from users_courses u, users
where u.users_id in (select distinct users_id from users_courses where courses_id = 2)
and u.courses_id = 1
and users.users_id = u.users_id
Almost similar to what #Nae's solution.
select u.name from users u
where exists
(select 1
from users_courses uc
where uc.course_id in (1, 2)
and uc.user_id = u.id
group by uc.user_id
having count(0) = 2);
Your code is close. Just use GROUP BY and a HAVING clause:
SELECT u.name
FROM users_courses uc JOIN
users u
ON uc.users_id = u.id JOIN
courses c
ON uc.courses_id = c.id
WHERE c.name IN ('History', 'Math')
GROUP BY u.name
HAVING COUNT(DISTINCT c.name) = 2;
Notes:
This assumes that users cannot have the same name. You might want to use GROUP BY u.id, u.name to ensure that you are counting individual users.
If users cannot take the same course multiple times, then use COUNT(*) = 2 rather than COUNT(DISTINCT).
I'd write:
SELECT MAX(u.name)
FROM users_courses uc
LEFT JOIN users u ON uc.users_id = u.id
WHERE uc.courses_id IN (1, 2)
GROUP BY uc.users_id
HAVING COUNT(0) = 2
;
For more complex conditions (for example requiring the user to be in certain classes but also not in certain classes such as "Science") this should also work:
SELECT MAX(u.name)
FROM users_courses uc
LEFT JOIN users u ON uc.users_id = u.id
GROUP BY uc.users_id
HAVING (
SUM(uc.courses_id = 1) = 1
-- user enrolled exactly once in the course 2
AND SUM(uc.courses_id = 2) = 1
-- user enrolled in course 3, 0 times
AND SUM(uc.courses_id = 3) = 0
)
;

SQL inner join on two colums

If i had two database tables
users -> (id, username)
and
messages -> (msg_id, sender_id, rec_id, text)
and they both joind on users.id = messages.sender_id and also on users.id = messages.rec_id so how i can print out results as follow
msg_id | sender | reciver | text
------------------------------------
1 | david | michael | hello friend
2 | eva | robert | pick me up
I've try this
SELECT users.*, messages.*
FROM messages
INNER JOIN users
ON users.id = messages.sender_id
AND users.id = messages.rec_id
but it seems not working as i want .. so any idea
update i ment by it deosn't seems to be working, that it gives the sender and reciver name are the same which is wrong !!
msg_id | sender | reciver | text
------------------------------------
1 | david | david | hello friend
2 | eva | eva | pick me up
You probably want to join multiple times
SELECT users1.*, users2.*,messages.*
FROM messages
INNER JOIN users1
ON users1.id = messages.sender_id
INNER JOIN users2
AND users2.id = messages.rec_id
You need two joins:
SELECT m.*, us.username as sender_name, ur.username as receiver_nae
FROM messages m JOIN
users us
ON us.id = m.sender_id JOIN
users ur
ON ur.id = m.rec_id;
In your current condition, the user id is equal to both the sender and the receiver id, meaning you will only query messages someone sent himself - which is probably not what you want to achieve. Instead, you can join on users twice, once for the sender and once for the receiver:
SELECT m.msg_id, s.username AS sender, r.username AS receiver, m.text
FROM messages m
JOIN users s ON m.sender_id = s.id
JOIN users r ON m.rec_id = r.id

LEFT JOIN in MySQL across multiple tables with NULL values

I have the following table structure with data
TABLE: USER
USER ID | USER NAME
1 | Joe
2 | Mary
TABLE : USER GROUP
USER ID | GROUP ID
1 | 1
1 | 2
TABLE : GROUP
GROUP ID | GROUP NAME
1 | Company 1
2 | Company 2
TABLE : ROLE
ROLE ID | ROLE NAME
1 | Administrator
2 | Users
TABLE : USER ROLE
USER ID | ROLE ID
1 | 1
2 | 1
As you can see user #2 does not belong to any group. Roles & Groups are optional forcing me to left joint but when I run a query as below
`SELECT a.user_id,
a.user_name
GROUP_CONCAT(r.role_name) AS role_names,
GROUP_CONCAT(g.group_name) AS group_names
FROM user a
LEFT JOIN role_map m ON a.user_id = m.user_id
INNER JOIN role r ON m.role_id = r.role_id
LEFT JOIN user_group s ON a.user_id = s.user_id
INNER JOIN group g ON s.group_id = g.group_id
GROUP BY a.user_id`
I get a cartesian product in the role_names column - the result looks like this
Joe | Administrators, Administrators | Company 1, Company 2
What am I doing wrong?
The easiest way to solve this is by using DISTINCT in your GROUP_CONCAT (SQL Fiddle). Also, you will need to add GROUP BY a.user_id in order to group per user:
SELECT a.user_id,
a.user_name,
GROUP_CONCAT(DISTINCT r.role_name) AS role_names,
GROUP_CONCAT(DISTINCT g.group_name) AS group_names
FROM `user` a
LEFT JOIN `user_role` m ON a.user_id = m.user_id
LEFT JOIN `role` r ON m.role_id = r.role_id
LEFT JOIN `user_group` s ON a.user_id = s.user_id
LEFT JOIN `group` g ON s.group_id = g.group_id
GROUP BY a.user_id;

Mysql queries issue

I have 3 tables in my mysql DB to query.
Users_rates (fields: id,userid,raterid,rate,eventid) containing all of the rates(rate) that have been assigned to users(userid), participating to specific events(eventid), by other users(raterid)
Events_participants (fields:id,userid,eventid) containing all of the users(userid) participating to each event(eventid)
Users (fields:id,name,lastname)containing all the user relative data
I need to query those three tables to retrieve an event-specific rank for the users' rates.
Ex. John,Erik and Mark participated to 'eventid=31'.
John received 1 rate from Mark, and 2 from Erik.
Mark received 1 rate from Erik.
Nobody has rated Erik though.
I need to retrieve for each user name,lastname and the sum of the rates received for eventid=31
I tried with this:
SELECT events_participants.userid,users.name,users.lastname,
(SELECT SUM(rate)FROM users_rates WHERE users_rates.eventid=31 AND users_rates.userid=events_participants.userid)AS rate
FROM(( events_participants INNER JOIN users ON events_participants.userid=users.id)
LEFT OUTER JOIN users_rates ON events_participants.userid=users_rates.userid )
WHERE events_participants.eventid=31
But I receive:
userid | name | lastname | rate
1 | luca | silvestro | 1
3 | claudio | buricchi | 6
3 | claudio | buricchi | 6
What's the right query?
Thanks
Luca
Try this:
SELECT users.userid, users.name, users.lastname, temp.sum as rate
FROM users LEFT JOIN (
SELECT userid, SUM(rate) as sum FROM users_rates WHERE eventid = 31 GROUP BY userid
) as temp USING (userid)
It might give an error, this might work instead:
SELECT users.userid, users.name, users.lastname, temp.sum as rate
FROM users, (
SELECT userid, SUM(rate) as sum FROM users_rates WHERE eventid = 31 GROUP BY userid
) as temp WHERE users.userid = temp.userid
I don't know if I got the problem right, but maybe something like:
SELECT u.id, u.name, u.lastname, SUM(ur.rate) AS rate
FROM users AS u
INNER JOIN users_rates AS ur ON ur.userid = u.id
WHERE ur.eventid = 31
GROUP BY u.id
edit: If you want to receive a list with all users regardless of whether they have any rates at all, you could also join the users_participants table and replace the INNER JOIN of users_rates by a LEFT JOIN. The WHERE clause has to reference events_participants then (not users_rates anymore as it could be NULL):
SELECT u.id, u.name, u.lastname, SUM(ur.rate) AS rate
FROM users AS u
INNER JOIN events_participants AS ep ON ep.userid = u.id
LEFT JOIN users_rates AS ur ON ur.userid = u.id AND ur.eventid = ep.eventid
WHERE ep.eventid = 31
GROUP BY u.id