(一)问题的提出

报表设计中,经常涉及层次结构的数据,比如产品的多级分类、组织的多级部门。如何展示层级结构本身,并分级汇总相关的数值数据,是报表设计人员经常面对的挑战。

下面是一个典型的部门表:

层级关系的关键是【上级部门ID】字段,该字段值为NULL的部门,就是顶级部门(根节点)。

关联的业务数据【部门业绩】表如下图:

其中,【部门ID】是引用部门表ID字段的外键字段。

表中记录只有最底层部门(叶节点)的销售业绩数据,上级部门的销售业绩需要根据层级关系逐级汇总。

(二)已知的方法与局限

网上推荐的做法是使用CTE递归查询:

with cte as

(

select [ID]

,[部门编码]

,[部门名称]

,[上级部门ID],0 as lvl from 部门

where [上级部门ID] is null

union all

select d.[ID]

,d.[部门编码]

,d.[部门名称]

,d.[上级部门ID],lvl+1 from cte c inner join 部门 d

on c.Id = d.[上级部门ID]

)

select * from cte

结果如下图:

这个查询结果可以清晰显示部门上下级关系,以及每个部门所处的层级。其中的lvl字段就是部门的层级,顶级部门层级为0。

但是要满足报表设计的要求,还需要解决几个问题:

(1)部门的显示次序,应按照上下级关系排列。上图中,【总务部】下属的【财务部】和【后勤部】应显示在第2、3行,而非图中显示的末尾。

(2)部门业绩应按部门上下级关系逐级汇总。

使用上面的递归查询,即使可以解决,也存在不易理解的问题。因此,下面采用一种通俗易懂的方法来逐步解决这些问题。

(三)解决方案

(1)部门排序问题

报表用户期待的部门排序方式,是一种按层级缩进的树状结构,如下图:

要实现这样的排序,需要保证一个部门的所有下级部门都排在这个部门的下面,并且仅当这个部门的子树全部显示完毕,才会显示其他部门。为此,我们引入一个【部门全路径】的概念,代表每个部门从根节点开始的各层级部门的组合,如下图:

以图中【西北一部】为例,该部门本身的ID是15,上级部门是【5-运营中心】\【7-销售部】\【10-西北大区】,因此部门全路径的ID就是-5-7-10-15,见图中的IDPath字段。

为了得到全路径部门ID,首先做几个CTE,分别是每个级别的部门列表,部门总共有几级,就做几个CTE,语句如下:

with CTE1 as

(

select 1 Lvl, T.ID, T.部门编码, T.部门名称,T.上级部门ID

, ID ID1, null ID2, null ID3, null ID4, null ID5, null ID6

from 部门 T where [上级部门ID] is null

)

, CTE2 as

(

select 2 Lvl, T.ID, T.部门编码, T.部门名称,T.上级部门ID

, CTE1.ID1, T.ID ID2, null ID3, null ID4, null ID5, null ID6

from 部门 T inner join CTE1 on T.[上级部门ID]=CTE1.ID

)

, CTE3 as

(

select 3 Lvl, T.ID, T.部门编码, T.部门名称,T.上级部门ID

, CTE2.ID1,CTE2.ID2,T.ID ID3,null ID4,null ID5,null ID6

from 部门 T inner join CTE2 on T.[上级部门ID]=CTE2.ID

)

,CTE4 as

(

select 4 Lvl, T.ID, T.部门编码, T.部门名称,T.上级部门ID

, CTE3.ID1,CTE3.ID2,CTE3.ID3,T.ID ID4,null ID5,null ID6

from 部门 T inner join CTE3 on T.[上级部门ID]=CTE3.ID

)

,CTE5 as

(

select 5 Lvl, T.ID, T.部门编码, T.部门名称,T.上级部门ID

,CTE4.ID1,CTE4.ID2,CTE4.ID3,CTE4.ID4,T.ID ID5,null ID6

from 部门 T inner join CTE4 on T.[上级部门ID]=CTE4.ID

)

,CTE6 as

(

select 6 Lvl, T.ID, T.部门编码, T.部门名称,T.上级部门ID

, CTE5.ID1,CTE5.ID2,CTE5.ID3,CTE5.ID4,CTE5.ID5,T.ID ID6

from 部门 T inner join CTE5 on T.[上级部门ID]=CTE5.ID

)

以上是按6级部门设计的6个CTE。

下一步,将各个级别的部门使用UNION合并在一起:

,CTE_Levels as

(

select * from CTE1

union

select * from CTE2

union

select * from CTE3

union

select * from CTE4

union

select * from CTE5

union

select * from CTE6

)

此时,select * from CTE_Levels的结果如下:

以此为基础,定义新的CTE,就可以查到每个部门的全路径ID了:

,CTE_Path as

(

select

L.ID1,T1.部门名称 名称

,L.ID2,T2.部门名称 名称

,L.ID3,T3.部门名称 名称

,L.ID4,T4.部门名称 名称

,L.ID5,T5.部门名称 名称

,L.ID6,T6.部门名称 名称

,L.ID

,L.部门名称 名称

,Lvl

,'-' + convert(nvarchar,ID1)

+ '-' + isnull(convert(nvarchar,ID2),'')

+ '-' + isnull(convert(nvarchar,ID3),'')

+ '-' + isnull(convert(nvarchar,ID4),'')

+ '-' + isnull(convert(nvarchar,ID5),'')

+ '-' + isnull(convert(nvarchar,ID6),'') + '-' IDPath

from CTE_Levels L

left join 部门 T1 on L.ID1=T1.ID

left join 部门 T2 on L.ID2=T2.ID

left join 部门 T3 on L.ID3=T3.ID

left join 部门 T4 on L.ID4=T4.ID

left join 部门 T5 on L.ID5=T5.ID

left join 部门 T6 on L.ID6=T6.ID

)

查询这个CTE,以全路径为排序依据:

select * from CTE_Path order by IDPath

结果如下:

这样的排列次序,就是符合预期的排序效果了。

(2)逐级汇总问题

基于全路径部门的CTE,定义新的CTE,使用JOIN,将部门信息与业绩数据关联起来:

,CTEx as

(

select T.ID,T.名称,T.IDPath,Lvl

,isnull(sum(M.销售业绩),0) 销售业绩

from CTE_Path T

left join 部门业绩 M on M.部门ID in

(select ID from CTE_Path where IDPath like '%-'

+ convert(nvarchar,T.ID) +'-%'  )

group by T.ID,T.名称,T.IDPath,Lvl

)

使用下面的查询语句来查询上面定义的CTE,可轻松实现销售业绩按部门分级汇总:

select ID,IDPath

,REPLICATE('__',lvl-1) + 名称 缩进名称

,销售业绩

from CTEx

--where IDPath like  ('%-' + convert(nvarchar,@DepID) +'-%')

order by IDPath

注意其中【缩进名称】的获取方法,借助CTE中代表部门级次的lvl字段值,在部门名称前添加不同数量的空格,级别越低的部门,前缀空格越多。

另请注意上述查询语句中注释掉的where子句,这是为了演示带参数的查询,即查询指定部门的销售业绩的方法。指定的DepID参数可以是任意级别的任意部门的ID,如果是一个非叶节点部门,查询结果将是该部门及其所有下属部门的业绩列表。

查询所有部门的业绩结果如下图:

这个查询结果可以直接用作报表设计中的数据集,报表效果如下图:

这就是真正符合用户预期的报表效果。

多层级部门结构展示与分级汇总相关推荐

  1. 利用递归查询部门表,以树(tree)的结构展示返回给前端出来,一般都要有这种需求的

    第一步写sql语句查询所有数据,或者根据条件查询所有需要的数据 我这边是根据创建的时间升序来排列的 <sql id="selectionCondition"><i ...

  2. 企业行业树形图,层级结构展示

    企业行业树形图,层级结构展示 一.业务需求 按照行业分类标准,实现行业间的层级关系,标准中行业分为四级,门类–大类–中类–小类,点击最后一层小类时,打开该类行业的查询结果页面,还有一点就是如果企业表中 ...

  3. js使用input上传文件夹、拖拽上传文件夹并将文件夹结构展示为树形结构

    一.实现效果 左侧区域支持选择一个系统中的文件夹,或者将文件夹拖拽到这个区域进行上传,右侧区域可以将文件夹的结构展示为树形结构. 二.代码实现 由于需要使用树形插件zTree,这个插件是依赖于jque ...

  4. 技术研发部部门结构及分工

    2018年11月 公司成立2年多了,技术部门一直作为公司的辅助性角色存在,领导也提出了准备扩展部门的意图.和部门领导交流了下意见,以下是个人根据交流意见及个人想法简要整理的一份部门组织结构及分工设想. ...

  5. NO.54 在禅道里建立部门结构、添加用户

    为什么80%的码农都做不了架构师?>>>    禅道安装成功之后,管理员的第一件要做的事情就是设置部门结构. 以管理员身份登录. 进入组织视图 选择部门维护. 在部门维护页面,维护公 ...

  6. 计算机工程学院文艺例会,信息科学与工程学院学生会学生会全体例会暨部门风采展示大会...

    原标题:信息科学与工程学院学生会学生会全体例会暨部门风采展示大会 2019 第27届学生会 部门风采 展示大会 2019年12月8日晚7:20,信息科学与工程学院学生会在弘毅①楼107大教室举办了学生 ...

  7. 前端多级组织(部门)结构展示

    预期效果: 开发: 插件:jOrgChart 扩展:jOrgChart-tree 下载:jOrgChart 实操: html <link rel="stylesheet" h ...

  8. java实现处理无限层级树形结构

    树形结构在实际业务中是很经常遇到的,比如说机构.菜单.部门等等业务就会经常遇到层级关系.一般层级处理,有两种方式 (1)将所有的数据返回给前端,由前端处理,组装成树形结构,别担心,前端有组件的,只要后 ...

  9. 部门名称部门结构叠用_金属结构分公司三部门联合开展工会小组活动

    为丰富职工的业余生活,营造轻松欢乐的氛围,增强部门间交流沟通,舒缓职工工作压力,在金属结构分公司工会的支持下,9月12日,分公司工程管理部.安全监管部和综合办公室联合开展工会小组活动,分公司总工程师白 ...

最新文章

  1. 【Python项目】CMDB的搭建12(SaltStack配置小结2/2)
  2. 设计模式之【外观模式-Facade】
  3. iptables详解(13):iptables动作总结之二
  4. mockito_吸收Mockito的流利度
  5. 升级php7_PhpStorm 2019.3 发布,全面支持 PHP 7.4
  6. jQuery之ajax的跨域获取数据
  7. 在linux上安装jenkins
  8. 【转】Tomcat中部署java web应用程序
  9. java开发项目经验_Java项目经验——程序员成长的钥匙
  10. php图片居中在div,css如何实现图片在div中垂直居中
  11. windows环境安装elasticsearch
  12. 迅雷SVIP版(资源下载神器)官方中文版V11.1.2.1078 | 迅雷不限速版下载
  13. Oracle12c使用AFD(Oracle ASM Filter Driver)特性部署集群的配置方法
  14. 40G SR4 vs 40G BIDI vs 40G UNIV:你选哪个?
  15. 小程序开发工具绑定服务器,微信小程序绑定到第三方平台流程
  16. cm-14.1 Android系统启动过程分析(4)-init进程的启动、rc脚本解析、zygote启动、属性服务
  17. 动态平衡网格交易_微笑每周答——定投基金,哪种方法最好?低估值、动态再平衡、网格交易法、价值平均策略,总有一款适合你...
  18. 文件储存树的理解(ISAM 和 B+Tree)
  19. Committer identity unknown *** Please tell me who you are...
  20. YL工作记录 (不断更新) -- 2020.04

热门文章

  1. 软件测试真的很重要!——软件测试的作用
  2. 除了乔布斯,科技圈还有哪些大佬值得充信仰?
  3. Linux第十节——信号
  4. 不止V神,全球最懂以太坊的人都来齐了,还差你
  5. 关于POI的一揽子问题
  6. 科研经验001:文献筛选下载和管理
  7. shell脚本编程 实例讲解
  8. 八年级英语下册计算机教学总结,初二信息技术教学工作总结
  9. memset函数定义及其使用
  10. 修改windows系统网卡的MAC地址方法