Using SQL STUFF to concatenate rows that I cannot group by - sql

I am trying to get a comma-delimited list of user roles for a specific app. I've used STUFF and FOR XML PATH('') for this problem before, but I don't know if I can in this situation. Here is the query that shows what I want to do, but doesn't work:
SELECT FName, LName, Email, Username,
STUFF((SELECT r.RoleDescription FROM Roles r2 WHERE r2.RoleID = r.RoleID FOR XML PATH('')),1,1,'') as RoleList
FROM Users u
INNER JOIN UserRole ur on u.UserID = ur.UserID
INNER JOIN Roles r on r.RoleID = ur.RoleID
WHERE ur.AppID = 506
GROUP BY FName, LName, Email, Username
This doesn't work because my where clause for the subquery contains a column that isn't in the GROUP BY. However if I group by the RoleID I lose the ability to combine those rows in the aggregate function.
Is there another way I can do this? Or a way that I could fix my query to achieve the result?

OUTER APPLY is far neater
SELECT FName, LName, Email, Username,
STUFF(x.csv,1,1,'') as RoleList
FROM Users u
INNER JOIN UserRole ur on u.UserID = ur.UserID
OUTER APPLY
(
SELECT
',' + r.RoleDescription
FROM
Roles r
WHERE
r.RoleID = ur.RoleID
FOR XML PATH ('')
) x (csv)
WHERE ur.AppID = 506
GROUP BY FName, LName, Email, Username, x.csv
However, you may not need group by now because you don't repeat per joined Role row
SELECT FName, LName, Email, Username,
STUFF(x.csv,1,1,'') as RoleList
FROM Users u
INNER JOIN UserRole ur on u.UserID = ur.UserID
OUTER APPLY
(
SELECT
',' + r.RoleDescription
FROM
Roles r
WHERE
r.RoleID = ur.RoleID
FOR XML PATH ('')
) x (csv)
WHERE ur.AppID = 506
For completeness, DISTINCT works better then GROUP BY for in-line sub queries because it is applied much later to the actual values. RoleId is not part of the actual values so won't break DISTINCT
Update for the filter which is actually on UserRole, which means we can use CROSS APPLY
SELECT FName, LName, Email, Username,
STUFF(x.csv,1,1,'') as RoleList
FROM Users u
CROSS APPLY
(
SELECT
',' + r.RoleDescription
FROM
UserRole ur
JOIN
Roles r ON ur.RoleID = r.RoleID
WHERE
u.UserID = ur.UserID
FOR XML PATH ('')
) x (csv)
WHERE u.UserID IN (Select UserID FROM UserRole WHERE AppID = 506)

Related

SQL Group By and Having clause and exists clause

These queries fetch records from multiple tables(AspNetUsers,AspNetUserRoles & AspNetRoles). The records will include only those users which have multiple Roles.
I am looking for reasons why 1st query works and the latter did not. Any help would be appreciated.
Query 1:
SELECT
U.Id,
U.UserName
,R.Id
,R.Name AS RoleName
FROM AspNetUsers AS U
JOIN AspNetUserRoles UR
ON U.Id = UR.UserId
JOIN AspNetRoles AS R
ON R.Id = UR.RoleId
WHERE EXISTS (
SELECT UserId,
COUNT() AS NumberofRoles
FROM AspNetUserRoles
GROUP BY UserId
HAVING COUNT() > 1)
Query 2:(Only work if I remove R.Id & R.Name Otherwise it is not working)
SELECT
U.Id,
U.UserName
,R.Id
,R.Name AS RoleName
FROM AspNetUsers AS U
JOIN AspNetUserRoles UR
ON U.Id = UR.UserId
JOIN AspNetRoles AS R
ON R.Id = UR.RoleId
GROUP BY U.Id,U.UserName
The table diagram is attached for better clarity.
The second query doesn't woek because you haven't defined anything known as R.
try this :
SELECT
U.Id,
U.UserName
,R.Id
,R.Name AS RoleName
FROM AspNetUsers AS U
JOIN AspNetUserRoles UR
ON U.Id = UR.UserId
JOIN AspNetRoles AS R
ON R.Id = UR.RoleId
GROUP BY U.Id,U.UserName,R.Id,R.Name
This would be the correct way to fetch records that have multiple Roles.
SELECT
U.Id,
U.UserName
,R.Id AS RoleID
,R.Name AS RoleName
FROM AspNetUsers AS U
JOIN AspNetUserRoles UR
ON U.Id = UR.UserId
JOIN AspNetRoles AS R
ON R.Id = UR.RoleId
WHERE U.Id IN (SELECT UserId FROM AspNetUserRoles GROUP BY UserId HAVING COUNT(*) > 1)

SQL QUERY disturbing Error

I have to write a query which displays the First and Last Name for all users who belong to the Role “Lumo Advantage”.
I wrote it as:
SELECT Users.FirstName, Users.LastName
INNER JOIN Users
ON UserRoles.UserID = Users.UserID
WHERE UserRoles.RoleID =7;
But it is showing error. why? Kindly point out the mistake.
Perhaps because you left out a from clause:
SELECT Users.FirstName, Users.LastName
FROM Users INNER JOIN
UserRoles
ON UserRoles.UserID = Users.UserID
WHERE UserRoles.RoleID = 7;
I would suggest you learn to use table aliases . . . so queries are easier to write and to read:
SELECT u.FirstName, u.LastName
FROM Users u INNER JOIN
UserRoles ur
ON ur.UserID = u.UserID
WHERE ur.RoleID = 7;

SELECT all from left, only from right only where

I have 3 tables, Roles, UsersInRoles and Users
I want to select all roles and a only user records where the user matches the where clause
SELECT r.*, u.UserName
FROM [dbo].[Roles] r
LEFT JOIN [dbo].[UsersInRoles ] ur
ON r.Id = ur.RoleId
LEFT JOIN [dbo].[Users] u
ON u.Id = ur.UserId
WHERE u.UserName = 'admin'
What I want is this:
RoleId, Role, Username
1 Admin Admin
2 Student Null
When I include the where clause, the student role is not returned
Move the filter from your WHERE clause, to the join condition of your Users table:
SELECT r.*, u.UserName
FROM [dbo].[Roles] r
LEFT JOIN [dbo].[UsersInRoles ] ur
ON r.Id = ur.RoleId
LEFT JOIN [dbo].[Users] u
ON u.Id = ur.UserId
AND u.UserName = 'admin'
This will return all records from Roles, with any matching records from UsersInRoles, but only those matching records in Users whose UserName equals 'admin'.
SELECT r.*, u.UserName
FROM [dbo].[Roles] r
LEFT JOIN [dbo].[UsersInRoles ] ur
ON r.Id = ur.RoleId
LEFT JOIN [dbo].[Users] u
ON u.Id = ur.UserId
WHERE (u.UserName = 'admin' or u.UserName is null)

List all friends for a userid SQL query

I have 2 tables a user table (user_id, fname, lname, dob, etc) and a are_friends table
(userA_id, userB_id). I have been trying to do this query for a while now, I need it to list all friends for a user_id.
What I have got so far,
SELECT
U.user_id,
U.fname,
U.lname
FROM are_friends A, user U
WHERE
A.user_id = U.user_id
AND (
A.user_id = 1
OR A.user_id IN (SELECT userB_id FROM are_friends WHERE userA_id = 1)
);
Any help will be much appreciated.
Try using an INNER JOIN like this:
SELECT u2.user_id, u2.fname, u2.lname
FROM user u
INNER JOIN are_friends f ON f.userA_id = u.user_id
INNER JOIN user u2 ON u2.user_id = f.userB_id
WHERE u.user_id = 1
You can change the WHERE clause to specifically get the friends of another user id.

SQL Get List of Users not in Roles

I have three tables:
Users : UserID, UserName
Roles : RoleID, RoleName
UsersInRoles : UserID, RoleID
How do I get a list of UserIDs and the RolesIDs for which they are NOT in?
I'm using SQL Server 2012.
Thanks for any help you can provide.
Aaron
select users.userid, roles.roleid
from users
cross join roles
left outer join usersinroles on
usersinroles.userid = users.userid and
usersinroles.roleid = roles.roleid
where usersinroles.userid is null
cross join joins each role to each user.
left outer join joins the tables, but doesn't delete the rows that don't match. Instead, it leaves the joined fields as null when there is no match. Getting only the cases where the field is null has the effect of getting only the rows that do not match--the roles that a user doesn't have.
I think #dan1111's solution is better, but this one might be more readable:
SELECT u.Userame,
r.RoleName
FROM Users u
CROSS JOIN Roles r
EXCEPT
SELECT u.Userame,
r.RoleName
FROM Users u
INNER JOIN UsersInRoles ur
on u.UserID = ur.UserID
INNER JOIN Roles r
on ur.RoleID = r.RoleID
Consider using a LEFT JOIN and combine it with an IS NULL condition.
This works using a CROSS JOIN and a LEFT JOIN:
SELECT U.UserId, R.RoleId
FROM Roles R CROSS JOIN Users U
LEFT JOIN UserRoles UR ON U.UserId = UR.UserId AND R.RoleId = UR.RoleId
WHERE UR.UserId IS NULL
And here is the sample Fiddle: http://sqlfiddle.com/#!6/46e48/1