SQL - nested joins - mysql

I have a mySQL database in which I'm trying to output a list of users and their assigned, administrative roles. My tables look something like:
Users:
-------
- id
- fname
- lname
Role_Names
-----------
- rn_id
- role_name
Roles
---------
- role_id
- user_id
here's some data:
Users:
-------
1 'Chris' 'Christy'
2 'Brian' 'Bobson'
3 'Jen' 'Sanders'
Role_Names
--------------
1 'admin'
2 'exec'
3 'employee'
Roles
-----------
1 1
1 2
1 3
2 3
3 3
3 2
and for my query, I'm using:
SELECT Users.fname, Role_Names.role_name from Users INNER JOIN
Roles on Users.id = Roles.user_id
INNER JOIN Role_Names
ON Roles.rn_id = Roles.role_id;
It only seems to be outputting roles for the 1st user_id in the Roles table. And it's outputting more than 1 of the same record. For example, my output looks like:
first_name role_name
--------------------------------------
Chris exec
Chris     exec
Chris exec
Chris employee
Chris employee
Chris employee
Chris admin
Chris admin
Chris admin
whereas I was hoping for something more like:
first_name role_name
--------------------------------------
Chris employee
Chris admin
Chris exec
Brian employee
Jen employee
Jen exec
...
At tis point I'm not sure if it's my table structure that is flawed or if I'm using joins incorrectly or if it's that plus a bunch of other stuff I don't even know about. Can someone help point me in the right direction?

Stare at this piece of your query: ON Roles.rn_id = Roles.role_id;
It is not what you meant!
Here is the fixed query (with clearer formatting):
SELECT Users.fname, Role_Names.role_name
FROM Users AS u
INNER JOIN Roles AS r ON u.id = r.user_id
INNER JOIN Role_Names AS rn ON rn.rn_id = r.role_id
Tip: Many:many mapping tables (your Roles) are typically named by the the two things it relates. So I suggest User_Roles. Then the 3rd table can be simply Roles. That leads to
SELECT Users.fname, Role_Names.role_name
FROM Users AS u
INNER JOIN User_Roles AS ur ON u.id = ur.user_id
INNER JOIN Roles AS r ON r.rn_id = ur.role_id

use distinct
with Users as
(
select 1 id, 'Chris' fname, 'Christy' lname
union all
select 2 , 'Brian', 'Bobson' union all
select 3 ,'Jen' , 'Sanders'
),Role_Names as
(
select 1 rn_id, 'admin' role_name
union all
select 2 , 'exec' union all
select 3 , 'employee'
) , Roles as
(
select 1 role_id,1 user_id
union all
select 1,2 union all
select 1,3 union all
select 2 , 3 union all
select 3 , 3 union all
select 3 , 2
) SELECT distinct Users.fname, Role_Names.role_name
from Users left JOIN
Roles on Users.id = Roles.user_id
left JOIN Role_Names
ON Role_Names.rn_id = Roles.role_id
you missed join in your query
JOIN Role_Names ON Roles.rn_id = Roles.role_id -- here both side you use Roles

You can use DISTINCT like following.
SELECT DISTINCT Users.fname, Role_Names.role_name from Users INNER JOIN
Roles on Users.id = Roles.user_id
INNER JOIN Role_Names
ON Roles.rn_id = Roles.role_id;

Try using below query.
SELECT Users.fname, Role_Names.role_name
from Roles RIGHT OUTER JOIN
Users on Users.id = Roles.user_id
INNER JOIN Role_Names
ON Roles_Names.rn_id = Roles.role_id;

Related

Inner Join with two columns

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

struggling with a sql query join

I am struggling with writing a query join in mysql
I have two table
Challenges
challenge_ID(int) |to_user(int)|from_user(int)|timestamp|gameID=>nullable
Users
iduser(int)|first_name(string)
I want get the first name of to_user and form_user when I have the challengeID
for instance if
Challenges
challenge_ID(int) |to_user(int)|from_user(int)|timestamp|gameID
1 9 10 sometimestamp
Users
iduser(int)|first_name(string)
9 Tom
10 Chris
11 Patrick
I would like to get 'Tom' and 'Chris' for challenge id 1
Thanks for your help.
It may be something like this:
SELECT first_name
FROM Users
WHERE iduser IN (SELECT to_user
FROM challenges
WHERE Challenge_Id = 1
UNION
SELECT from_user
FROM challenges
WHERE Challenge_Id = 1)
this is how you do this
select c.* ,u.first_name as to_name , u2.first_name as from_name
FROM challenges c
join users u on c.to_user = u.id
join users u2 on c.from_user = u2.id
where c.challenge_ID = 1
It looks like you need a UNION and then a JOIN:
SELECT users.first_name
FROM (
SELECT from_user AS usr FROM challenges WHERE challenge_id=1
UNION
SELECT to_user FROM challenges WHERE challenge_id=1
) u INNER JOIN users ON u.usr = users.id_user
UNION will remove duplicates on the subquery, if there are no duplicates you can use UNION ALL which is faster.
You can try this query
SELECT u_to.first_name, u_from.first_name
FROM challenges c
INNER JOIN users u_to ON u_to.iduser = c.to_user
INNER JOIN users u_from ON u_from.iduser = c.from_user
WHERE c.challange_ID = 1

MySQL select query from two tables

I have two tables: users and works
I need write select query for count different names from users table where work_status = 1 from works table
The total is: 3 John, 1 Tom
I need get result:
John 2 (2 because one John work_status = 0 ant this not counting)
Tom 1
I have write select that can count different names, just need compared work_status..
SELECT name,COUNT(*) as num FROM users GROUP BY name
My query return:
There is a problem in your question. So here you have two solutions.
If there are three different John working on the company, this is your query
SELECT u.name, COUNT(*) as num
FROM users u INNER JOIN works w ON w.user_id = u.id
WHERE w.work_status = 1
GROUP BY u.name, u.id
If there are only one John working in the company, your query is this one:
SELECT u.name, COUNT(*) as num
FROM users u INNER JOIN works w ON w.user_id = u.id
WHERE w.work_status = 1
GROUP BY u.name
Note: If three John are the same person, you should delete the 2 last and on the works table change user_id = 3 and user_id = 4 for user_id = 1
This is a simple JOIN query:
SELECT u.name, COUNT(*) num
FROM users u
JOIN works w
ON w.user_id = u.id
AND w.work_status = 1
GROUP BY u.name
This one should do the job:
SELECT users.name,SUM(works.work_status) as num
FROM users,works
WHERE users.id=works.id
GROUP BY name
SELECT
users.`name`,
COUNT(*) num
FROM
users,
works
WHERE users.`id` = works.`user_id`
AND works.`work_status` = 1
GROUP BY users.`name` ;

MYSQL compare values from same columns

Okay I tried to look all over stackoverflow, and the closest solution I found is this:
mysql AND clause on same column multiple times
But I can't use statements and "having" syntax won't work because of group by. There MUST be a simple solution to this.
The 2 tables looks like this (simplified):
users:
uid name
1 person 1
2 person 2
3 person 3
categories:
uid value
1 actor
1 musician
2 actor
3 dancer
4 musician
4 dancer
I want to get the uid of those that are 2 values at the same time. For example, I want to get the UID that is an actor AND a musician. Not just one value, but both of them must be required!
First I tried this:
SELECT users.uid, users.name
FROM
users
LEFT OUTER JOIN categories ON users.uid = categories.uid
WHERE (categories.value = 'actor' AND categories.value = 'musician')
GROUP BY u.uid;
This of course does not work since one row can't have 2 values.
Does anyone know a solution?
You can JOIN to the categories table multiple times to get the result:
SELECT users.uid, users.name
FROM users
INNER JOIN categories c1
ON users.uid = c1.uid
INNER JOIN categories c2
ON users.uid = c2.uid
WHERE c1.value = 'actor'
AND c2.value = 'musician';
See SQL Fiddle with Demo
SELECT users.uid, users.name
FROM users
LEFT JOIN categories ON users.uid = categories.uid
WHERE categories.value in ('actor', 'musician')
GROUP BY u.uid, users.name
having count(distinct categories.value) = 2;
Use a having clause
SELECT u.uid, u.name
FROM users u
LEFT OUTER JOIN categories c ON u.uid = c.uid
WHERE c.value = 'actor' OR c.value = 'musician'
GROUP BY u.uid
having count(distinct c.value) > 1
If you really do not want to use having you could try this:
SELECT uid, name
FROM users
WHERE
uid IN (SELECT uid FROM categories WHERE value='actor')
AND uid IN (SELECT uid FROM categories WHERE value='musician')
But there is really nothing wrong with using HAVING ;)

SELECT all contacts (friends) of a user

I have the following database:
user_id and contact_id from Contacts table are PK and FK to the user_id field in the Users table. How can i select all contacts (or friends) of a specific user including the number of contacts of all contacts of this user. I tried different SELECT queries but either the number of contacts is incorrect or the contact_status is printed wrong. I use COUNT() as a function to print the number of contacts. I use this query but the contact_status is printed wrong:
SELECT COUNT(Contacts.contact_id), Users.user_id, Users.user_name, Users.name, Users.user_picture, Users.mood_message, Users.phone_number, Users.email, Users.country, Users.city, Users.website, Users.birth_date, Users.gender, Users.language, Users.about_me, Users.online_status, Users.privacy, Contacts.contact_status
FROM Contacts JOIN Users ON Contacts.contact_id = Users.user_id
WHERE Users.user_name IN (
SELECT Users.user_name
FROM Users
WHERE Users.user_id IN (
SELECT Contacts.contact_id
FROM Contacts
WHERE Contacts.user_id = 12
)
)
GROUP BY Users.user_name;
Users Table:
user_id, user_name, ...
12 John ...
13 Matt ...
14 Jack ...
Contacts Table:
user_id, contact_status, contact_id
12 1 13
13 1 12
12 2 14
If i want to print all Contacts of John the result should consist:
COUNT(Contacts.contacts_id), Users.user_name, Users. ... , Contacts.contact_status
1 Matt ... 1
0 Jack ... 2
The above query prints 1, 1 as a contact_status instead of 1, 2.
Can you help me with this query? Thanks in advance.
If I understood what's your goal, this should work:
SELECT c.contact_status, cc.contactCount, u.*
FROM Contacts c JOIN Users u ON (c.contact_id = u.user_id)
JOIN (
SELECT user_id, COUNT(1) contactCount FROM Contacts
GROUP BY user_id
) cc ON (c.user_id = cc.user_id)
WHERE c.user_id = 12
I hope I understand ur requirements, but its much simpler to use a subquery, like so:
SELECT
u.* -- Whatever fields you need
,(
select count(*) from Contacts c
where c.contactid = co.userid -- refers to outer record
) as contact_count
FROM
Users u Inner Join Contacts co
on co.UserId = u.UserId
WHERE
U.UserId = 12