按条件选择行:a和(b xor c)

时间:2019-07-05 11:51:21

标签: sql

我有3张这样的桌子:

User:
Id | Name

Role:
Id | Name

User2Role:
RoleId | UserId | RoleName

有没有一种方法可以选择在没有子查询的查询中具有RoleName'A'和('B'xor'C')的用户,因此仅AB或AC,而不仅仅是BC而不是A等。

到目前为止,我只能想到这一点:

select U.Name, R.Name
from User2Role UR join User U on UR.UserId = U.Id
                  join Role R on UR.RoleId = R.Id
where R.Name = 'A'

添加多个EXISTS,将其变成丑陋的混乱。 但是还有更优雅的解决方案吗?

2 个答案:

答案 0 :(得分:1)

您可以尝试以下操作:

with role_abc as (
    select 
        u.id, u.name,
        max(decode(r.name, 'A', 1, 0)) as has_a,
        max(decode(r.name, 'B', 1, 0)) as has_b,
        max(decode(r.name, 'C', 1, 0)) as has_c
    from usr u
         inner join user2role ur on ur.userid = u.id
         inner join role r on r.id = ur.roleid
    group by u.id, u.name
)
select id, name from role_abc
where has_a = 1 and has_b + has_c = 1

条件非常简洁明了-以with子句的价格为准。

如果还需要角色名称,则可以轻松加入他们。

由于在oracle上进行了测试,因此不得不将表用户的名称缩写为usr。

答案 1 :(得分:0)

您可以使用having子句。假设角色没有重复:

select ur.userid
from user2role ur join
     roles r
     on ur.roleid = r.id
where r.name in ('A', 'B', 'C')
group by ur.userid
having sum(case when r.name = 'A' then 1 else 0 end) > 0 and
       count(*) = 2;   -- two matches overall

更明确的having子句为:

having sum(case when r.name = 'A' then 1 else 0 end) = 1 and
       sum(case when r.name in ('B', 'C') then 1 else 0 end) = 1