SQL SELECT statement with users, roles and rights - sql

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

Related

INSERT INTO not working with my select

I need to copy some data from one table to another:
There is a table that has the correct date (AUDITLOG), column TIME but I need to put it in the USER table, column USERS_DATE and associate with the correct user...
The SELECT returns the correct data by itself, I'm having issue using the SELECT INTO statement. My hacked up code below:
INSERT INTO users (USERS_DATE) WHERE USERID=U.USERID
(SELECT U.USERID,
javaTimeStampToDate (L.TIME)
AS "Last Login"
FROM COMPANY C,
USERS U,
AUDITMAP M,
AUDITLOG L
WHERE C.COMPANYID = U.COMPANYID
AND U.USERID = M.ROWID
AND M.AUDITID = L.AUDITID
AND C.APPLICATION = 'A'
AND L.NOTES LIKE '%went inactive%'
AND U.STATUS = 0);
Try with the below query for sql server.
I think you need an update query. Also change your commas with JOIN condition.
UPDATE U
SET U.USERS_DATE=L.TIME
FROM COMPANY C
JOIN USERS U ON C.COMPANYID = U.COMPANYID
JOIN AUDITMAP M ON U.USERID = M.ROWID
JOIN AUDITLOG L ON M.AUDITID = L.AUDITID
WHERE C.APPLICATION = 'A'
AND L.NOTES LIKE '%went inactive%'
AND U.STATUS = 0
use below query for oracle,
MERGE INTO USERS U
USING
(
SELECT M.ROWID,L.Time
FROM AUDITMAP M
JOIN AUDITLOG L ON M.AUDITID = L.AUDITID
WHERE L.NOTES LIKE '%went inactive%'
) Au ON (U.USERID = Au.ROWID)
WHEN MATCHED THEN UPDATE
SET U.USERS_DATE = Au.TIME
WHERE U.STATUS = 0 AND EXISTS (select 1
from COMPANY c
where .COMPANYID = U.COMPANYID AND C.APPLICATION = 'A')

SQL Subquery column equals operation

I am trying to do a SQL query for user with certain permission enabled flag. I know, I can do this:
select u.ID, u.Name,
(select p.Value
from Permissions p
where p.UserID = u.ID AND p.Key = 'CanEdit') as IsPermissionEnabled
from Users u
But it's not exactly what I need, can I do something like this:
select u.ID, u.Name,
((select p.Value
from Permissions p
where p.UserID = u.ID AND p.Key = 'CanEdit') = 'True')
as IsPermissionEnabled
from Users u
It didn't work for me. So, how to change my query to make it work?
Surely you should just join to the table to get p.Value.
Then you can do with it whatever you like:
SELECT
u.ID,
u.Name,
p.Value as IsPermissionEnabled
FROM Users u
LEFT OUTER JOIN Permissions p
ON p.UserID = u.ID
AND p.Key = 'CanEdit';
Try this query
select u.ID, u.Name, case when p.value>0 then 'True' else '' end
as IsPermissionEnabled
from Users u
left join permission p on p.UserID = u.ID and p.key='CanEdit'
In SQL Server, you need an explicit case statement. So, you can write the query as:
select u.ID, u.Name,
(case when (select p.Value
from Permissions p
where p.UserID = u.ID AND p.Key = 'CanEdit'
) = 'True'
then 1 else 0
end) as IsPermissionEnabled
from Users u;
A join ( inner or left) with a Case can be used to return the desired data with IsPermissionEnabled. Assuming intent is to treat IsPermissionEnabled as boolean, the case statement can set it to a bit value of 0 or 1 as below.
select u.USERID , u.Username, IsPermissionEnabled = case p.[Key] when 'CanEdit' then 1 else 0 end from [Permissions] p inner join [User] u on u.USERID = p.userid

Better ways to write this SQL Query

I have the below table structure
Users (PK - UserId)
System (PK - SystemId)
SystemRoles (PK-SystemRoleId, FK - SystemId)
UserRoles (PK-UserId & SystemRoleId, FK-SystemRoleId, FK-UserId)
Users can have access to different Systems and one System can have different SystemRoles defined.
Now, I need to delete Users who have SystemRoles assigned to them ONLY for a specific System(s). If they have SystemRoles defined for other Systems, they should not be deleted.
I have come up the below query to identify the records that are eligible for delete but think this can surely be optimized. Any suggestions?
SELECT U.*
FROM
(
SELECT
distinct UR.UserID
FROM
dbo.UserRole UR
INNER JOIN dbo.SystemRole SR ON (SR.SystemRoleID = UR.SystemRoleID)
INNER JOIN dbo.[System] S ON (S.SystemID = SR.SystemID)
WHERE
S.SystemName = 'ABC' OR S.SystemName = 'XYZ'
) T
INNER JOIN dbo.[User] U ON (U.UserID = T.UserID)
WHERE T.UserID NOT IN
(
select
distinct UR.UserID
from
dbo.[UserRole] UR
INNER JOIN dbo.SystemRole SR ON (SR.SystemRoleID = UR.SystemRoleID)
INNER JOIN dbo.[System] S ON (S.SystemID = SR.SystemID)
WHERE
S.SystemName <> 'ABC'
AND S.SystemName <> 'XYZ'
)
something like this?
select userid from (
SELECT
UR.UserID,
max(case when (S.SystemName = 'ABC' OR S.SystemName = 'XYZ')
then 1 else 0 end) as kill,
max(case when (S.SystemName <> 'ABC' AND S.SystemName <> 'XYZ')
then 1 else 0 end) as keep
FROM
dbo.UserRole UR
INNER JOIN dbo.SystemRole SR ON (SR.SystemRoleID = UR.SystemRoleID)
INNER JOIN dbo.[System] S ON (S.SystemID = SR.SystemID)
group by UR.UserID
) u where kill = 1 and keep = 0
This sort of structure will get you the records you need.
select yourfields -- or delete
from userroles
where userid in
(select userid
from userroles join etc
where system.name = the one you want
except
select userid
from userroles join etc
where system.name <> the one you want
)

SQL Server : JOIN query

I'm newbie in SQL Server and I need some help with SQL Server and JOIN method.
My code is:
SELECT TOP 1000
p.value AS userposition,
p2.value AS usercell,
t.id
FROM
[es] t
JOIN
[user] u ON t.user_uid = u.uid
JOIN
[user] su ON u.superior_uid = su.uid
JOIN
[user_params] up ON t.user_uid = up.user_uid
LEFT JOIN
[params] p ON up.param_id = p.id AND p.name_id = 1
JOIN
[user_params] up2 ON t.user_uid = up2.user_uid
LEFT JOIN
[params] p2 ON up2.param_id = p2.id AND p.name_id = 2
but it returns duplicated records. I want them just as many as rows in [es] table. In MySQL I would use GROUP BY t.id, but in SQL Server that method doesn't work.
Thanks in advance.
EDIT (clarification):
Thank you for your replies. Maybe I should describe my tables structure and what I need to display.
Table [ES]
[id],[user_uid],[more_data]
Table [User]
[uid],[superior_uid],[more_data]
Table [UserParams]
[id],[user_uid],[param_id]
Table [Params]
[id],[param_id],[value]
Now what I need is to get all records from [ES] add user data from [User] add his superior data on [User][superior_uid] which is also an [User] record, add [Params] with [Params][name_id] = 1 as value1 AND add [Params] with [Params][name_id] = 2 as value2 ... through [UserParams] if exists.
I think the problem is with JOIN or GROUP BY. [ES] records with users has no [UserParams] are shown only once, but those with [UserParams] are doubled.I tried LEFT OUTER JOIN but it doesn't work. :(
How about
SELECT DISTINCT TOP 1000
p.value AS userposition,
p2.value AS usercell,
t.id
FROM [es] t
JOIN [user] u ON t.user_uid = u.uid
JOIN [user] su ON u.superior_uid = su.uid
JOIN [user_params] up ON t.user_uid = up.user_uid
LEFT JOIN [params] p ON up.param_id = p.id AND p.name_id = 1
JOIN [user_params] up2 ON t.user_uid = up2.user_uid
LEFT JOIN [params] p2 ON up2.param_id = p2.id AND p.name_id = 2
ORDER BY (whichever rows that you want it to be ordered by) ?
all of your columns need to be in the group by, or part of an aggregate function
p.value AS userposition, #group by or agg func
p2.value AS usercell, #group by or agg func
t.id #group by
Wouldnt be certain without knowing what p.value and p2.value actually mean
Not to sure about the joins but I guess you know what you doing, to select distinct row you can use row_number() function as below
SELECT userposition
,usercell
,id
FROM (
SELECT TOP 1000
p.value AS userposition
,p2.value AS usercell
,t.id
,ROW_NUMBER() OVER (PARTITION BY t.user_uid ORDER BY t.user_uid) rn
FROM [es] t
INNER JOIN [user] u ON t.user_uid = u.[uid]
INNER JOIN [user] su ON u.superior_uid = su.[uid]
INNER JOIN [user_params] up ON t.user_uid = up.user_uid
LEFT JOIN [params] p ON up.param_id = p.id AND p.name_id = 1
INNER JOIN [user_params] up2 ON t.user_uid = up2.user_uid
LEFT JOIN [params] p2 ON up2.param_id = p2.id AND p.name_id = 2
) A
WHERE A.rn = 1
You can get rid of the last 2 joins by combining them, use DISTINCT to not get repeated entries, and use ORDER BY to sort it.
SELECT DISTINCT TOP 1000
p.value AS userposition,
p2.value AS usercell,
t.id
FROM [es] t
JOIN [user] u ON t.user_uid = u.uid
JOIN [user] su ON u.superior_uid = su.uid
JOIN [user_params] up ON t.user_uid = up.user_uid
LEFT JOIN [params] p ON up.param_id = p.id AND (p.name_id = 1 OR p.name_id = 2)
ORDER BY t.id

How to return multiple values when using SELECT EXISTS in postgresql

I have the following SQL query:
SELECT EXISTS (SELECT r.id FROM Rules r INNER JOIN rule_t c on c.id=r.rule_t.id
INNER JOIN user u on u.id = r.user_id
WHERE u.fmnum='2813'
AND c.name='default') ::int
Is there a way I can modify this so that I get two values back, the INT from the EXISTS method, and r.id?
I know that I can change the query so that I remove the EXISTS method... if the sub select returns anything at all, then I know the record exists... but I'm just wondering if its possible to do the above.
Thanks.
EDIT 1
I'm testing the following code in a new query window in pgadmin3...
SELECT *
FROM (
SELECT TRUE, r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
);
But I'm getting the following error:
ERROR: subquery in FROM must have an alias LINE 2: (
^ HINT: For example, FROM (SELECT ...) [AS] foo.
EDIT 2
SELECT *
FROM (
SELECT TRUE, r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
) AS x;
SELECT 1 AS does_exist, r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
LIMIT 1; -- may or may not be needed.
This does what you seem to be asking for: you get two columns. But you get no row if nothing is found.
If you want a row, even if nothing is found, you need a subquery:
SELECT sub.t_id IS NOT NULL AS does_exist, sub.id
FROM (SELECT 1) x -- dummy to guarantee 1 row
LEFT JOIN ( -- LEFT JOIN is crucial
SELECT r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
LIMIT 1 -- may or may not be needed.
) ON TRUE; -- join condition is always true
Or, simpler / faster:
SELECT 1 AS does_exist, r.id
FROM rules r
JOIN rule_t c on c.id = r.rule_t.id
JOIN user u on u.id = r.user_id
WHERE u.fmnum = '2813'
AND c.name = 'default'
UNION ALL
SELECT 0, NULL
LIMIT 1;