I have 5 tables: user > user_has_role > role > role_has_permission > permission
An user can have 0 or * roles.
And a role can have 0 or * permissions
now i have a query which will get all permissions belonging to a user.
This work but now i want that if the user has the role "super_admin" then all permissions get joined. But without specifing them in the table role_has_permission.
This is query:
SELECT p.name,r.name role
FROM user_has_role AS ur
JOIN role AS r
ON ur.role_id = r.id
JOIN role_has_permission AS rp
ON r.id = rp.role_id
LEFT JOIN permission AS p
ON rp.permission_id = p.id
WHERE ur.user_id = [USER_ID]
You could just do a union:
SELECT p.name,r.name role
FROM user_has_role AS ur
JOIN role AS r
ON ur.role_id = r.id
JOIN role_has_permission AS rp
ON r.id = rp.role_id
LEFT JOIN permission AS p
ON rp.permission_id = p.id
WHERE ur.user_id = [USER_ID]
UNION
SELECT p.name, 'super_admin'
FROM permission
WHERE EXISTS (SELECT * FROM user_has_role, role
WHERE ur.user_id = [USER_ID] and role.role_id = user_has_role.role_id and
role.name = 'super_admin')
Related
I have a table of users who have certain roles. A user is entered into the table one time for each role they have. I need to get a count of all users who have certain roles but I need to exclude any duplicate record that also has another role. Below is what is populated in my table
Name Role
Steve ROLE_8
Steve ROLE_9
Steve ROLE_1
And this is the query I have to select users who have certain roles. What I need to do is check to see if a user has ROLE_1 but also check if there is another instance of that user who has a role that I do not wish to include and exclude that user from the return set.
SELECT COUNT(DISTINCT c.user_id)
FROM users c
WHERE email_addr != ''
AND email_addr IS NOT NULL
AND EXISTS
(SELECT r.role_id
FROM ROLE r, user_role ur
WHERE c.user_id = ur.user_id
AND ur.role_id = r.role_id
AND (r.name = 'ROLE_1'
OR r.name = 'ROLE_2'
OR r.name = 'ROLE_3'
OR r.name = 'ROLE_4'
OR r.name = 'ROLE_5'
OR r.name = 'ROLE_6'))
Using the DISTINCT in your COUNT makes the EXISTS unnecessary - you can just join to the user_role table. At that point you just need to exclude those users who also have one of the roles that you don't want:
SELECT
COUNT(DISTINCT U.user_id)
FROM
Users U
INNER JOIN User_Role UR ON UR.user_id = U.user_id AND
INNER JOIN Role R ON
R.role_id = UR.role_id AND
R.name IN ('ROLE_USER_ADMIN', 'ROLE_1'...)
WHERE
U.email_addr IS NOT NULL AND U.email_addr <> '' AND
NOT EXISTS
(
SELECT *
FROM
User_Role UR2
INNER JOIN Role R2 ON
R2.role_id = UR2.role_id AND
R2.name IN ('Some_Excluded_Role')
WHERE
UR2.user_id = U.user_id
)
If you want to exclude any user who has any role outside of your list then you can do the following:
SELECT
COUNT(DISTINCT U.user_id)
FROM
Users U
INNER JOIN User_Role UR ON UR.user_id = U.user_id AND
INNER JOIN Role R ON
R.role_id = UR.role_id AND
R.name IN ('ROLE_USER_ADMIN', 'ROLE_1'...)
WHERE
U.email_addr IS NOT NULL AND U.email_addr <> '' AND
NOT EXISTS
(
SELECT *
FROM
User_Role UR2
INNER JOIN Role R2 ON
R2.role_id = UR2.role_id AND
R2.name NOT IN ('ROLE_USER_ADMIN', 'ROLE_1'...)
WHERE
UR2.user_id = U.user_id
)
I believe this will get you what you're looking for (without knowing exactly what you expect the results to look like). The concept being to first find the ones that match the 'good' roles and then filter with the other sub-select the ones that also have 'bad' roles.
SELECT COUNT(DISTINCT c.[user_id])
FROM users AS c
INNER JOIN user_role AS ur
ON ur.[user_id] = c.[user_id]
WHERE c.email_addr != ''
AND c.email_addr IS NOT NULL
AND ur.role_id IN
(SELECT sub_r.role_id
FROM [role] AS sub_r
WHERE sub_r.name IN ('ROLE_USER_ADMIN', 'ROLE_1')
)
AND c.[user_id] NOT IN
(SELECT sub2_ur.[user_id]
FROM user_role AS sub2_ur
INNER JOIN [role] AS sub2_r
ON sub2_r.role_id = sub2_ur.role_id
WHERE sub2_r.name IN ('ROLE_NOT_TO_USE','ANOTHER_NOT_TO_USE')
AND sub2_ur.[user_id] = c.[user_id]
)
If I wanted to get a dataset in which I'm including users that have either role 1, 2, or 3 and I wanted to exclude users that had role 4 and 5, I would do the following, if "Name" is unique:
SELECT COUNT(DISTINCT Name)
FROM ROLE
WHERE Role IN ('ROLE_1','ROLE_2','ROLE_3')
AND Name NOT IN (SELECT DISTINCT Name FROM ROLE WHERE Role IN ('ROLE_4','ROLE_5');
I have 3 tables in sqlserver :
tbl_Users - User_ID, User_Name
tbl_Roles - Role_ID, Role_Name
tbl_Users_Roles_MTM - User_ID, Role_ID
A user can have multiple roles assigned to him, and that will show in the Many-To-Many table.
In my stored-procedure I need Role_Name which are NOT assigned to a specific User_ID (which is given as a parameter).
I guess I should use an INNER JOIN (or a LEFT one...).
There are numerous entries in SO and other forums with questions nearly similar to this but not quite. I experimented a lot but by now I completely lost my hands and feet!
Thank you all.
EDIT :
With the help of the good people of SO, I got it to work :
SELECT r.Role_Name
FROM tbl_Roles r
WHERE NOT EXISTS(
SELECT 1
FROM tbl_Users_Roles_MTM ur
WHERE ur.User_ID = #User_ID
AND ur.Role_ID = r.Role_ID);
SO people are awesome!!!!!
Try this query:
SELECT r.Role_Name
FROM tbl_Roles r
WHERE NOT EXISTS (
SELECT 1
FROM tbl_Users_Roles_MTM ur
WHERE ur.User_ID = #User_ID
AND ur.Role_ID = r.Role_ID);
You can do this:
SELECT *
FROM Roles
WHERE Role_Name = #rolename
AND NOT EXISTS(SELECT 1
FROM tbl_Roles AS r
INNER JOIN tbl_Users_Roles_MTM AS ur ON ur.Role_ID = r.Role_ID
WHERE r.role_Name = #rolename
AND ur.User_ID = #User_ID);
The NOT EXISTS predicate will ensure that the role is not assigned to any user.
SELECT r.*
FROM tbl_Roles r
LEFT JOIN tbl_Users_Roles_MTM ur
ON r.Role_ID = ur.Role_ID
AND ur.User_ID = #User_ID
WHERE ur.User_ID IS NULL
I would do something like this
select roles.Role_Name
from tbl_Roles as roles
left join tbl_Users_Roles_MTM as userRoles
on userRoles.Role_ID = roles.Role_ID
and userRoles.[User_ID] = #User_ID
where userRoles.Role_ID is null
Basically left join and then select only those rows, where it has not been joined
I was wondering how to simplify this query, here(http://sqlfiddle.com/#!3/2789c/4) you have the complete example
SELECT distinct (R.[roleId])
FROM [Role] R
LEFT JOIN [userRole] U ON R.[roleId] = U.[roleId]
WHERE R.RoleID NOT IN(
SELECT [roleId]
from [dbo].[userRole]
WHERE userId = 2)
I want to get all the roles that are not assigned to an specific user. I think the inner select could be erase.
Update 1
After your great help, I could use only one SELECT http://sqlfiddle.com/#!3/2789c/87
SELECT R.[roleID]
FROM [Role] R
LEFT JOIN [userRole] U
ON R.[roleID] = U.[roleID] AND U.userId = #userID
WHERE U.userId IS NULL
As simple as it gets:
select roleId
from Role
except
select roleId
from userRole
where userId = 2
SELECT R.roleId
FROM [Role] R
LEFT JOIN [userRole] U ON R.roleId = U.roleId
group by r.roleId
having sum(case when U.userId = 2 then 1 else 0 end) = 0
SQLFiddle demo
You can also try this
Select distinct role.roleID from role , userrole
except
select roleId from userrole where userID=2
OR
SELECT R.roleId
FROM [Role] R
LEFT JOIN [userRole] U ON R.roleId = U.roleId
except
select roleId from userrole where userID=2
Lets say that I have tables:
Users
Users_in_Roles
Roles
Rights_in_Roles
Rights
Keys are standard( UserFk, RoleFk, RightFk)
The question is: how to get all users that are in role with right X (id = 100)
I Have no idea how to touch this problem. Please help and sorry for my english.
SELECT [dbo].[System_Users].[Id]
,[UserName]
,[FirstName]
,[LastName]
,[Email]
,RoleFk
,[dbo].[System_Roles].Name
FROM [dbo].[System_Users]
INNER JOIN [dbo].[System_Roles_System_Users]
ON [dbo].[System_Roles_System_Users].UserFk = [dbo].[System_Users].Id
INNER JOIN [dbo].[System_Roles]
ON [dbo].[System_Roles].Id = [dbo].[System_Roles_System_Users].RoleFk
WHERE ?
I tryied sth like that, could you tell me what iw wrong?
SELECT
DISTINCT System_Users.Id,
System_Users.FullName
FROM System_Users
INNER JOIN Dict_Rights_System_Users
ON System_Users.Id = Dict_Rights_System_Users.UserFk
INNER JOIN System_Roles_System_Users
ON System_Roles_System_Users.UserFk = System_Users.Id
WHERE
RightFk = 136
OR
136 IN (SELECT Dict_Rights_System_Roles.RightFk FROM Dict_Rights_System_Roles WHERE
Dict_Rights_System_Roles.RoleFk = System_Roles_System_Users.RoleFk)
ORDER BY System_Users.FullName ASC
You will need to JOIN the tables on the key relationships. The basic structure will be:
select u.Id,
u.UserName,
u.FirstName,
u.LastName,
u.Email,
r.RoleFk,
r.Name RoleName,
rt.Name RightName
from users u
inner join users_in_roles ur
on u.id = ur.userfk
inner join roles r
on ur.rolefk = r.id
inner join rights_in_roles rr
on r.rolefk = rr.rolefk
inner join rights rt
on rr.rightfk = rt.id
where rt.id = 100
If you need help learning JOIN syntax here is a great reference:
A Visual Explanation of SQL Joins
You can try with this:
SELECT *
FROM Users u
WHERE EXISTS (
SELECT ur.RoleFk
FROM Users_in_Roles ur
WHERE u.UserPk = ur.UserFk
AND EXISTS
(
SELECT 1
FROM Rights_in_Roles rr
WHERE rr.RoleFk = ur.RoleFk
AND rr.RightFk = 100
)
)
OR EXISTS (
SELECT 1
FROM Users_Rights uri
WHERE u.UserPk = uri.UserFk
AND uri.RightFk = 100
)
Note that the above query doesn't return RoleFk and Name for the role.
Another approach would be:
SELECT u.Id
,u.UserName
,u.FirstName
,u.LastName
,u.Email
,rr.RoleFk
,r.Name
FROM Users u
-- get users that are in role that has right
LEFT JOIN
Users_in_Roles ur ON
ur.UserFk = u.UserPk
LEFT JOIN
Rights_in_Roles rr ON
rr.RoleFk = ur.RoleFk
AND rr.RightFk = 100
LEFT JOIN
Rights r ON
r.RolePk = rr.RoleFk
-- get users that have a right granted to them directly
LEFT JOIN
Users_Rights uri ON
u.UserPk = uri.UserFk
AND uri.RightFk = 100
WHERE rr.RoleFk IS NOT NULL OR uri.UserFk IS NOT NULL
I want to write a script which will show all messages owned by ONLY users with RoleID = 4. So I tried to write something like this:
SELECT DT.DiscussionThreadID, DT.Message FROM DiscussionThread DT
INNER JOIN Users U on U.UserID = DT.CreatedBy
INNER JOIN UserRoles UR on UR.UserID = U.RoleID
WHERE UR.RoleID = 4
Example of UserRoles table:
UserID RoleID
1 1
1 2
1 4
2 4
3 3
3 4
I expected to see only messages posted by user with UserID = 2 - he don't have additional roles except of RoleID = 4. But my script returns all posts. Can someone help me?
SELECT DT.DiscussionThreadID, DT.Message
FROM DiscussionThread DT
INNER JOIN Users U on U.UserID = DT.CreatedBy
INNER JOIN UserRoles UR on UR.RoleID = U.RoleID
group by DT.DiscussionThreadID, DT.Message
having count(Distinct roleID) = 1
and max(roleID)=4
If you want better performance, do the role check before the join to DiscussionThread. You can also dispense with the users table, since fields from the table are not being used:
SELECT DT.DiscussionThreadID, DT.Message
FROM DiscussionThread DT inner join
(select userId
from UserRoles UR
group by userId
having COUNT(distinct roleId) = 1) and max(roleId) = 4
) ur
on UR.UserID = U.UserID
group by DT.DiscussionThreadID, DT.Message