双重职责问题

  问题来自于《SQL puzzles and answers》一书的第36个Puzzle。问题的描述很简单,书中给出了很多种解答,我只能想到其中的1、2种,故在这里进行一下分享。有意思的是书中的解法1是无法通过SQL解析的,大家可以查看原书并进行尝试。本文中的解法在SQL Server 2008中测试通过,可能与原书有部分差异。

  问题描述

  我们有一张权责表:

person role
Smith O
Smith D
Jones O
White D
Brown X

  表中包含2列,person列中存储人名,role列中存储职责代码,O代表Officer,D代表Director等等。创建表的脚本:

    
    
    
    
use tempdb;

create table Roles
(
person
char ( 5 ) not null ,
role
char ( 1 ) not null
);

insert into
Roles
(
person,
role
)
values
(
' Smith ' , ' O ' ),
(
' Smith ' , ' D ' ),
(
' Jones ' , ' O ' ),
(
' White ' , ' D ' ),
(
' Brown ' , ' X ' );

需要写一个查询,只关心role为O和D的人。若有人既有职责代码O,又有职责代码D,则合并显示为B。上表的查询结果应为:

person combined_role
Smith B
Jones O
White D

解决方案1

    
    
    
    
With DirectorRole as
(
select
person,
role
from
Roles
where
role
= ' D '
),
OfficerRole
as
(
select
person,
role
from
Roles
where
role
= ' O '
)
select
coalesce (DirectorRole.person, OfficerRole.person) as person,
case when
(DirectorRole.person
is not null and OfficerRole.person is not null )
then
' B '
else
coalesce (DirectorRole.role, OfficerRole.role)
end as combined_role
from
DirectorRole
full outer join
OfficerRole
on
DirectorRole.person
= OfficerRole.person;


解决方案2

     
     
     
     
select
person,
' B ' as combined_role
from
Roles
where
role
in ( ' O ' , ' D ' )
group by
person
having
COUNT ( * ) = 2
union all
select
person,
max (role) as combined_role
from
Roles
where
role
in ( ' O ' , ' D ' )
group by
person
having
COUNT ( * ) = 1

解决方案3

     
     
     
     
select distinct
R1.person,
case when exists ( select
*
from
Roles
as R2
where
R2.person
= R1.person
and
R2.role
<> R1.role
and
R2.role
in ( ' D ' , ' O ' ))
then ' B '
else R1.role
end as combined_role
from
Roles
as R1
where
R1.role
in ( ' O ' , ' D ' );

解决方案4

    
    
    
    
select
person,
case when
COUNT ( * ) = 1
then
max (role)
else
' B '
end as combined_role
from
Roles
where
role
in ( ' D ' , ' O ' )
group by
person;

解决方案5

   
   
   
   
select
person,
case when MIN (role) <> MAX (role)
then ' B '
else MIN (role)
end as combined_role
from
Roles
where
role
in ( ' D ' , ' O ' )
group by
person;

解决方案6

   
   
   
   
select
person,
SUBSTRING ( ' ODB ' , sum ( charindex (role, ' OD ' , 1 )), 1 ) as combined_role
from
Roles
where
role
in ( ' D ' , ' O ' )
group by
person;

总结

  从这个例子里可以充分感受到SQL的丰富性,大家可以进一步比较各方法的性能。第6个方案相对前几个方案难理解一些,我在这里多说两句。此查询首先按照人名来分组(这点很重要)。若某人只有一个O职责,charindex函数将返回1;若某人只有一个D职责,charindex函数将返回2;若某人既有O又有D职责,charindex函数将返回2条记录值分别为1和2,经外层聚合函数sum求和后为3。之后substring再将sum的结果值作为索引取‘ODB’中的一个字母。

你可能感兴趣的:(双重职责问题)