SQL View merge with multiple tables - mysql

I have multiple tables of roles (e.g. student, guest, teacher) and a table of users with their id's.
I want to make a SQL view which has the userId and Role ("student", "teacher" ...) as a string.
The roles tables each have a foreign userId but don't have the role as text string. So there should be some kind of comparator, if the user has an id in student then its role is "student".
How would you start writing this ?

Assuming that each of the role tables has 0 or 1 records per user, you could use a series of left joins:
SELECT
u.id,
CASE
WHEN s.id IS NOT NULL THEN 'student'
WHEN g.id IS NOT NULL THEN 'guest'
WHEN t.id IS NOT NULL THEN 'teacher'
ELSE 'unknown'
END role
FROM users u
LEFT JOIN students s ON s.id = u.id
LEFT JOIN guests g ON g.id = u.id
LEFT JOIN teacher t ON t.id = u.id
If a user may have more than one role, you could split the information over several boolean columns:
SELECT
u.id,
CASE
s.id IS NOT NULL is_teacher,
g.id IS NOT NULL is_guest,
t.id IS NOT NULL then is_teaacher
FROM users u
LEFT JOIN students s ON s.id = u.id
LEFT JOIN guests g ON g.id = s.id
LEFT JOIN teacher t ON t.id = t.id

Is this what you want?
SELECT u.*,
(CASE WHEN EXISTS (SELECT 1 FROM students s WHERE s.user_id = u.user_id)
THEN 'student'
WHEN EXISTS (SELECT 1 FROM guests g WHERE g.user_id = u.user_id)
THEN 'guest'
WHEN EXISTS (SELECT 1 FROM teachers t WHERE t.user_id = u.user_id)
THEN 'teacher'
ELSE '???'
END) AS role
FROM users u;
This assumes that each user has only one role. This can be modified (using concat()) to handle multiple roles.

Related

GROUP_CONCAT() like function to return fields that don't match?

I have three tables in my database, for the purposes of discussion let's say they are:
USERS
-----
user_id
user_name
ROLES
-----
role_id
role
USER_ROLES
----------
user_role_id
user_id
role_id
I can easily use GROUP_CONCAT() to return a comma separated list of roles the user does have like so:
SELECT u.user_id, u.user_name, GROUP_CONCAT(role) AS roles_held
FROM users u,
roles r,
user_roles ur
WHERE u.user_id = ur.user_id
AND r.role_id = ur.role_id
GROUP BY u.user_id, u.user_name;
What I'd like to do is also return a list of roles the user does not have.
My initial thought was using sub-query then concatenating that, but I can't seem to get that to work. Can someone point me in the right direction?
Thank you!
EDIT: To clarify, the desired output would be a query that returns the user_id, user_name, a concatenated string of the roles the user does have and a concatenated string of the roles the user does not have. So for example:
USER_ID USER_NAME ROLES_HELD ROLES_LACKED
1 Bob User,Writer Editor,Admin
2 Doug User Writer,Editor,Admin
I agree with using a CROSS JOIN here, but it doesn't need to be quite so complicated:
SELECT
u.user_id,
u.user_name,
GROUP_CONCAT(case when ur.user_id is not null then r.role end) AS roles_held,
GROUP_CONCAT(case when ur.user_id is null then r.role end) as roles_lacked
FROM
users u
CROSS JOIN roles r
LEFT JOIN user_roles ur ON
u.user_id = ur.user_id
AND r.role_id = ur.role_id
GROUP BY
u.user_id, u.user_name
You could try to join table role two times The first time to get held roles, the second time to get lacked roles.
SELECT u.user_id,
user_name,
GROUP_CONCAT(DISTINCT r.role) AS role_held,
GROUP_CONCAT(DISTINCT r1.role) AS role_lacked
FROM users u
INNER JOIN user_roles ur
ON u.user_id = ur.user_id
INNER JOIN roles r
ON r.role_id = ur.role_id
INNER JOIN roles r1
ON NOT EXISTS (SELECT 1
FROM user_roles ur1
WHERE user_id = u.user_id
AND role_id = r1.role_id)
GROUP BY u.user_id,
user_name
I've created a demo here

Fetching data in case of one -to- many relationship in MySQL

I have 3 tables users, user_group, and groups.
users have one to many relationship with groups.
If I want to fetch only those users who don't have group Mathematics.
I have using the following query for this purpose:
SELECT * FROM users
INNER JOIN user_group ON user_group.user_id = user.UserID
INNER JOIN groups ON user_group.group_id = groups.group_id
WHERE groups.Name <> 'Mathematics';
But it is returning multiple records against all Users. Suppose, if I have user John and he joined 3 groups Science, Mathematics and English. In this case, it will return two records of user John. I want to remove user John totally from the list.
You can use NOT EXISTS:
SELECT *
FROM users AS u
WHERE NOT EXISTS (SELECT 1
FROM user_group AS ug
INNER JOIN groups AS g ON ug.group_id = g.group_id
WHERE ug.user_id = u.UserID AND g.Name = 'Mathematics');
Demo here
If you want to do it using joins, then this is a way:
SELECT u.*
FROM users AS u
LEFT JOIN (
SELECT user_id
FROM user_group
INNER JOIN groups
ON user_group.group_id = groups.group_id AND groups.Name = 'Mathematics'
) AS g ON u.UserID = g.user_id
WHERE g.user_id IS NULL
Demo here
SELECT * FROM users
LEFT JOIN user_group ON user.UserID = user_group.user_id
LEFT JOIN groups ON user_group.group_id = groups.group_id
WHERE groups.Name != 'Mathematics';

select from 3 tables with null values

I have 3 tables
Users
COLUMNS User Name, USER ID
Groups
COLUMNS Group Name,Group Id
Users_Group
COLUMNS User ID, Group ID
the User group contains the relation between Users and groups.
I want to select from users and groups to get user names and group names togother.
But I want also to get the users who dont have group where the return value of group name will be null
How to create such SQL in mysql
You need to left join as
select
u.username,
g.group_name
from users u
left join users_group ug on ug.user_id = u.user_id
left join groups g on g.group_id = ug.group_id
select u.username,
g.groupname
from users u
left join user_group ug on u.userid=ug.userid
left join groups g on g.groupid=ug.groupid

SQL query to link two tables returning error

I have two tables as follows
user [ ID , username ]
relationship [ user1_id(FK) , user2_id (FK) , status ]
I am trying to get the username by using either user1_id or user2_id where the status = 1 from relationship table. user1_id and user2_id are both IDs from the user table. The following query is failing and I am not sure where it's going wrong.
SELECT
U.username,
(R.first_user_id, R.second_user_id AS friends)
FROM
user U,
`relationship` R
WHERE (R.`first_user_id` = {$userID} OR R.`second_user_id`)
AND (`status` = 1 AND U.ID = friends)
returns both names of users in a relationship with a status of 1.
this also assumes that if a relationship record exists, both users must be in the user table.
SELECT U1.UserName, U2.username
FROM Relationship R
INNER JOIN USER U1
on R.User1_ID = U1.user_ID
INNER JOIN USER U2
and R.User2_ID = U2.user_ID
WHERE R.Status=1
It looks like you may be trying to get the usernames of all users that have a relationship with a certain specified user, regardless of the order of user IDs in the relationship record. That could be this:
SELECT
U.username,
U.first_user_id,
FROM
user U
JOIN `relationship` R
ON R.first_user_id = U.ID
WHERE
(R.`second_user_id` = {$userID})
AND (`status` = 1)
UNION ALL
SELECT
U.username,
U.second_user_id,
FROM
user U
JOIN `relationship` R
ON R.second_user_id = U.ID
WHERE
(R.`first_user_id` = {$userID})
AND (`status` = 1)
If that produces duplicates (or could do) and you don't want it to do, then change the UNION ALL to a straight UNION.
I have succeeded based on xQbert answer:
SELECT
U1.username,
U2.username
AS user_friend
FROM
Relationship R
INNER JOIN
user U1
ON
R.first_user_id = U1.ID
INNER JOIN
user U2
ON
R.second_user_id = U2.ID
WHERE (R.`first_user_id` = {$userID} OR R.`second_user_id` = {$userID})
AND `status` = 1

How to construct a command to join 3 different tables in mySQL?

I have three different tables in MySQL:
given an user id, how can i get a list of role's name of this user?
For example, user_id = 1
I need a list like this (1, deleteuser, modifyuser, viewuser)
How can I construct my SQL command to get such a list?
JOIN the three tables like so:
SELECT
u.id,
r.Name
FROM user u
INNER JOIN user_role ur ON u.id = ur.user_id
INNER JOIN roles r ON ur.Role_id = r.id
WHERE u.id = Someid
However, if you want the list of roles for each user to be concatenated into one string. Use GROUP_CONCAT like so:
SELECT
u.id,
GROUP_CONCAT(r.Name) roles
FROM user u
INNER JOIN user_role ur ON u.id = ur.user_id
INNER JOIN role r ON ur.Role_id = r.id
WHERE u.id = 1
GROUP BY u.id
SQL Fiddle Demo
SELECT User_id, Name
FROM User_role
JOIN Role ON User_role.Role_id = Role.Id
WHERE User_id = '1'
that would create a list like this:
User_id | Name
1 | DeleteUser
1 | ModifyUser
1 | ViewUser
Try this:
SELECT User_id, GROUP_CONCAT(r.Name)
FROM User_Role ur
INNER JOIN Role r ON ur.Role_id = r.Role_id
GROUP BY User_id;