SQL Subquery column equals operation - sql

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

Related

SQL ad-hoc report of ALL users not completed a course

Hya Gang!
I am very close to figuring out a report I can run ad hoc to find all our users who have not enrolled in a course. I do have a report for enrolled people that have not completed the course, but this search is not finding a given student who is not even enrolled.
The current code is
SELECT u.lastname, u.firstname , u.email , c.fullname,
DATE_FORMAT(FROM_UNIXTIME(cc.timecompleted),'%m/%d/%Y %T') AS 'Completed'
FROM
prefix_role_assignments AS ra
JOIN prefix_context AS context ON context.id = ra.contextid
AND
context.contextlevel = 50 JOIN prefix_course AS c ON c.id = context.instanceid
AND
c.fullname LIKE "SAMPLE_COURSE_NAME"
JOIN prefix_user AS u ON u.id = ra.userid
JOIN prefix_course_completions AS cc ON cc.course = c.id
AND cc.userid = u.id
ORDER BY
cc.timecompleted,
u.lastname,
u.firstname
Any thoughts??
This will list all users not enrolled in a course - could be a big list though.
Replace xxx with the course id
SELECT u.id AS userid, u.firstname, u.lastname, u.email
FROM mdl_user u
WHERE u.deleted = 0
AND u.suspended = 0
AND NOT EXISTS (
SELECT ue.userid
FROM mdl_user_enrolments ue
JOIN mdl_enrol e ON e.id = ue.enrolid AND e.courseid = xxx
WHERE ue.userid = u.id
)
Update... I found using "mdl_" will not work within the moodle framework, but you can replace it with "prefix_" and IT WORKS! Thank you SO much! I never would have thought of this setup.
SELECT u.id AS userid, u.firstname, u.lastname, u.email
FROM prefix_user u
WHERE u.deleted = 0
AND u.suspended = 0
and firstname not like "Guest user"
AND NOT EXISTS (
SELECT ue.userid
FROM prefix_user_enrolments ue
JOIN prefix_enrol e ON e.id = ue.enrolid AND e.courseid = XXX
WHERE ue.userid = u.id
)
ORDER BY
Lastname,
Firstname

How to simplify a query between Many to Many Relationship

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

SQL SELECT statement with users, roles and rights

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

Using a left join and checking if the row existed along with another check in where clause

I have the following tables:
Users
Banned
SELECT u.*
FROM Users
WHERE u.isActive = 1
AND
u.status <> 'disabled'
I don't want to include any rows where the user may also be in the Banned table.
What's the best way to do this?
I could do this put a subquery in the where clause so it does something like:
u.status <> 'disabled' and not exist (SELECT 1 FORM Banned where userId = #userId)
I think the best way would be to do a LEFT JOIN, how could I do that?
According to this answer, in SQL-Server using NOT EXISTS is more efficient than LEFT JOIN/IS NULL
SELECT *
FROM Users u
WHERE u.IsActive = 1
AND u.Status <> 'disabled'
AND NOT EXISTS (SELECT 1 FROM Banned b WHERE b.UserID = u.UserID)
EDIT
For the sake of completeness this is how I would do it with a LEFT JOIN:
SELECT *
FROM Users u
LEFT JOIN Banned b
ON b.UserID = u.UserID
WHERE u.IsActive = 1
AND u.Status <> 'disabled'
AND b.UserID IS NULL -- EXCLUDE ROWS WITH A MATCH IN `BANNED`
You would just check that the value you got from LEFT JOINing with Banned was NULL:
SELECT U.*
FROM Users U
LEFT JOIN Banned B ON B.userId = U.userId
WHERE U.isActive = 1
AND U.status <> 'disabled'
AND B.userId IS NULL -- no match in the Banned table.
select u.*
from Users u
left outer join Banned b on u.userId = b.userId
where u.isActive = 1
and u.status <> 'disabled'
and b.UserID is null
SELECT u.*
FROM Users u
LEFT JOIN Banned b ON u.userId = b.userId AND b.userRoles = 'VIP'
WHERE u.isActive = 1 AND b.id IS NULL
Use it if You need result and something should be excluded and it is not a key id for table.

How to place an ORDER BY clause in SQL between UNIONS

I want to implement simple SQL query that will return a sorted list. The problem is that I get syntax errors for placing the ORDER BY clause anywhere I put it.
SELECT
fr.FunctionRoleID, fr.FunctionRoleInternalName
FROM
users u
JOIN
UserRoles ur ON ur.UserID = u.UserID
JOIN
Roles_FunctionRoles rfr ON rfr.RoleID = ur.RoleID
JOIN
FunctionRoles fr ON fr.FunctionRoleID = rfr.FunctionRoleID
WHERE
u.UserName = #UserName
AND u.Active = 1
UNION
SELECT
fr.FunctionRoleID, fr.FunctionRoleInternalName
FROM
Roles r
JOIN
Roles_FunctionRoles rfr ON rfr.RoleID = r.RoleID
JOIN
FunctionRoles fr ON fr.FunctionRoleID = rfr.FunctionRoleID
WHERE
r.RoleName = 'Authenticated Users'
AND #UserName IS NOT NULL
AND LEN(#UserName) > 0
What I want to insert:
ORDER BY fr.DisplayName ASC
EDIT
If I create a subquery using
SELECT *
FROM
(
[my initial query]
)
ORDER BY
[COLUMN NAME] ASC
I get the following error message:
Incorrect syntax near 'ORDER'. Expected 'AS', 'ID' or 'QUOTED_id'
In most databases, you can only place an order by at the end of a union.
Because the union abstracts away individual table aliases, you only have to list the column name. So omit the fr. :
ORDER BY DisplayName
The ORDER BY clause needs to be placed after the last SELECT statement of the Union.
select * from(
*what you have there*
) as foo
order by DisplayName ASC
I'm not in front of an IDE so the syntax may be off a bit but that's the idea.
e: yeah, figured I'd jack up the syntax...alias added :)
Your not selecting DisplayName so you cannot use it to ORDER BY a set derived from a UNION. If you want to order by it and omit it from the results;
;WITH T (FunctionRoleID, FunctionRoleInternalName, DisplayName) AS (
SELECT
fr.FunctionRoleID, fr.FunctionRoleInternalName, fr.DisplayName
FROM users u
JOIN UserRoles ur ON ur.UserID = u.UserID
JOIN Roles_FunctionRoles rfr ON rfr.RoleID = ur.RoleID
JOIN FunctionRoles fr ON fr.FunctionRoleID = rfr.FunctionRoleID
WHERE
u.UserName = #UserName
AND
u.Active = 1
UNION
SELECT
fr.FunctionRoleID, fr.FunctionRoleInternalName, fr.DisplayName
FROM
Roles r
JOIN Roles_FunctionRoles rfr ON rfr.RoleID = r.RoleID
JOIN FunctionRoles fr ON fr.FunctionRoleID = rfr.FunctionRoleID
WHERE
r.RoleName = 'Authenticated Users'
and #UserName is not null and LEN(#UserName) > 0
)
SELECT
FunctionRoleID, FunctionRoleInternalName
FROM T
ORDER BY DisplayName
Try
SELECT * FROM (
SELECT
fr.FunctionRoleID, fr.FunctionRoleInternalName
FROM
users u
JOIN UserRoles ur ON ur.UserID = u.UserID
JOIN Roles_FunctionRoles rfr ON rfr.RoleID = ur.RoleID
JOIN FunctionRoles fr ON fr.FunctionRoleID = rfr.FunctionRoleID
WHERE
u.UserName = #UserName
AND
u.Active = 1
UNION
SELECT
fr.FunctionRoleID, fr.FunctionRoleInternalName
FROM
Roles r
JOIN Roles_FunctionRoles rfr ON rfr.RoleID = r.RoleID
JOIN FunctionRoles fr ON fr.FunctionRoleID = rfr.FunctionRoleID
WHERE
r.RoleName = 'Authenticated Users'
and #UserName is not null and LEN(#UserName) > 0 ) a
ORDER BY a.FunctionRoleInternalName ASC
Basically, you are selecting against the result of the UNION and then performing the ORDER BY...note that I use "FunctionRoleInternalName"...you can change that to "DiaplayName" only of you use that as a column ALIAS in the UNION queries...e.g. "FunctionRoleInternalName AS DisplayName"
For UNION, ORDER BY goes at the end and applies to the combined result of both queries; you can't order by a column that is not selected by both queries in the union.
what you need to do is select fr.DisplayName in both queries; then you can order by it.
If you don't want the display name to be one of the output columns, nest the whole thing in an outer query that retrieves just the columns you want.