There are two tables; X_User_Role and Role like this
X_User_Role table:
************************
UserRoleID UserID RoleID
************************
1 1 1
************************
2 1 2
************************
3 2 1
Role table:
********************************
RoleID Name SecondaryFl
********************************
1 PrimaryRole 0
********************************
2 SecondaryRole 1
********************************
Now I am writing a select statement with joins so that in my final output it should come as (considering only 1 user ID) :
UserID PrimaryRoleName SecondaryRoleName
***********************************************************
1 PrimaryRole SecondaryRole
I have tried joining like this:
select xur.UserID, r1.Name as PrimaryRoleName, r2.Name as SecondaryRoleName
from X_User_Role xur
JOIN Role r1
ON r1.RoleID = xur.RoleID
JOIN Role r2
ON r2.RoleID = r1.RoleID AND SecondaryFl = 1
But I always get output like this:
UserID PrimaryRoleName SecondaryRoleName
***********************************************************
1 PrimaryRole PrimaryRole
I have tried different variations of the above joins but I never get the desired output. I am not able to figure out what I am doing wrong. I am a complete novice at SQL. Can anyone please assist?
Note: I have to use JOINS because this is a part of a bigger select which is entirely made up of JOINS.
Conditional aggregation should be used
select xur.userId,
max(case when SecondaryFl = 0 then r.name end) as PrimaryRoleName,
max(case when SecondaryFl = 1 then r.name end) as SecondaryRoleName
from X_User_Role xur
join Role r on r.RoleID = xur.RoleID
group by xur.userId;
Edit by (YogeshSharma)
Always include where
clause while doing the conditional aggregation as because in case SecondaryFl
column has more value other than above then it will include null rows always.
select xur.userId,
max(case when SecondaryFl = 0 then r.name end) as PrimaryRoleName,
max(case when SecondaryFl = 1 then r.name end) as SecondaryRoleName
from X_User_Role xur
join Role r on r.RoleID = xur.RoleID
where SecondaryFl in (1, 0) -- Always include especially for conditions aggregation
group by xur.userId;
There's a few facts missing from the question I think. Like, what qualified a role to be seconday and primary? But for this answer I will assume that the SecondaryFl
field indicates this. 0 for primary and 1 for secondary.
I believe that the problem you are seeing is due to this from you query:
JOIN Role r2
ON r2.RoleID = r1.RoleID AND SecondaryFl = 1
Since you are joining the Role table a second time, on the same primary key. Instead you want to join it again, but with it's own conditions.
Like this:
select
user_role.UserID,
role_primary.Name as PrimaryRoleName,
role_secondary.Name as SecondaryRoleName
from X_User_Role as user_role
join Role as role_primary
on role_primary.RoleID = user_role.RoleID and SecondaryFl = 0
join Role as role_secondary
on role_secondary.RoleID = user_role.RoleID and SecondaryFl = 1
Sorry that I changed your names, I just find it more readable. It should illustrate the point clearer here I think.
EDIT: I assumed that the roles would always be present here. In case that roles are optional, you could use a left join
instead, and handle returned nulls with something like coalesce(role_primary.Name, 'None')
or just treat the returned null where you are receiving the results.
Like this:
select
user_role.UserID,
coalesce(role_primary.Name, 'None') as PrimaryRoleName,
coalesce(role_secondary.Name, 'None') as SecondaryRoleName
from X_User_Role as user_role
left join Role as role_primary
on role_primary.RoleID = user_role.RoleID and SecondaryFl = 0
left join Role as role_secondary
on role_secondary.RoleID = user_role.RoleID and SecondaryFl = 1
You can use a pivot query like below see live demo
On your note:
I have to use JOINS because this is a part of a bigger select which is entirely made up of JOINS.
you can always encapsulate below query as a nested query in a JOIN
select
UserId,
PrimaryRoleName=[0],
SecondaryRoleName=[1]
from
(
select
X.UserID,
Name,
SecondaryFl
from
X_User_Role X
left join Role R
on X.RoleID=R.RoleID
) src
pivot
(
max(Name) for SecondaryFl in ([0],[1])
)p
if you absolutely want a JOIN syntax, you can try below query
select
UserID=X.UserID,
PrimaryRoleName=MAX(case when SecondaryFl=0 then Name else NULL end),
SecondaryRoleName=MAX(case when SecondaryFl=1 then Name else NULL end)
from
X_User_Role X
left join Role R
on X.RoleID=R.RoleID
group by X.UserID
Returning a dynamic number of columns? You are looking for trouble.
You should return the roles, one per row. A simple SQL join will do.
This is what I was suggesting in my comment:
select xur.UserID, r1.Name as PrimaryRoleName, r2.Name as SecondaryRoleName
from X_User_Role xur
JOIN Role r1
ON r1.RoleID = xur.RoleID AND r1.SecondaryFl <> 1
JOIN Role r2
ON r2.RoleID = xur.RoleID AND r2.SecondaryFl = 1
WHERE xur.UserID=1
Just wanted to post another answer that doesn't resort to GROUP BY
:
select x1.userid, r1.name, r2.name
from x_user_role x1, role r1, x_user_role x2, role r2
where r1.roleid = x1.roleid and r1.secondaryfl = 0
and x1.userid = 1
and r2.roleid = x2.roleid and r2.secondaryfl = 1
and x2.userid = x1.userid
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.