zoukankan      html  css  js  c++  java
  • 双重职责问题

    问题来自于《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’中的一个字母。

  • 相关阅读:
    webkit特有的css属性
    js apply/call/caller/callee/bind使用方法与区别分析
    canvas
    seajs
    div 等高
    js常用函数
    浮动元素水平居中
    JavaScript的9个陷阱及评点
    Touch Event
    js跨域问题
  • 原文地址:https://www.cnblogs.com/DBFocus/p/1924420.html
Copyright © 2011-2022 走看看