简体   繁体   中英

sql getting values from the same table and same column into one row

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.

  • What if the user has three, four, five roles?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM