sql 小计合计排序

很久没写东西了!

学校班级学号姓名分数
学校A班级0101小林90
学校A班级0102小李60
 班级小计人数2150
学校A班级0201小强75
 班级小计人数175
 学校合计人数3225
学校B班级0101小小85
 班级小计人数185
 学校合计人数185
 全县合计学校数:2学生数:4总分:305

像这样的列表里面出现小计和合计的情况在统计报表中是经常出现的, 在这些报表里面, 怎么排序就是一个难题了!

上面的表格, 学生的排序是按学号的大小排序的, 班级目前是也是使用大小排序, 但是学校不是大小排序, 而是固定排序, 对于这种情况, 那么我们就需要自己手工给学校做一个序号, 方便他们排序了.

但是怎么把"班级小计","学校合计"和"全县合计" 插入到正确的位置呢?

我们正常的排序是:     order by  学校, 班级, 学号

  • "班级小计" 是要放在某个班级的最后一位学生后面, 最后一位是号码是什么, 我们可以求出来, 但是这样写起来每次都要进行一次最后一位的判断! 这样显然是低效和繁琐的.
  • "学校合计" 是要放在某个学校的班级信息的最后一位.
  • "全县合计" 是要放在某个县的学校信息的最后一位.

我们可以从中看出, "最后一位" 这个是关键, 怎么确定最后一位呢?  老师可以帮我们安排学位, 为什么不能帮我们的小计直接安排在最后呢? 学校可以安排班级顺位, 那由它直接安排合计在最后不就行了吗? 所以

  • "班级小计" order by 学校,班级
  • "学校合计" order by 学校

由统计的范围来决定小计合计的排序位置, 简单来说,

" 学校A ->班级01->班级合计列"  直接赋予"班级010" 这样的信息,  这样通过order by 学校,班级 , 班级01 的"班级小记" 就会在该班级中排最后(当然不是说一定在后面加"0"的, 具体要加什么可以由自己决定, 可以参考一些排序优先级的,如果是oracle, null 默认是最大值, 要注意不要让班级01的班级小记掉到其他班级那里去了 ).

" 学校A->学校合计" 直接赋予"学校A0" 这样的信息, 理由同上.

 

学校班级
县层面排序值
(自定义的)
学校排序值班级排序值学号姓名分数
学校A班级0101学校A班级0101小林90
学校A班级0101学校A班级0102小李60
 班级小计01学校A班级010人数2150
学校A班级0201学校A班级0201小强75
 班级小计01学校A班级020人数175
 学校合计01学校A0 人数3225
学校B班级0102学校B班级0101小小85
 班级小计02学校B班级010人数185
 学校合计02学校B0 人数1
85
 全县合计99  学校数:2学生数:4总分:305

注意里面的 蓝色字体,    这样就简单了

   order 县层面排序值,学校排序值,班级排序值, 学号

一般思路:

select 调整显示内容

from (

      select * from

       (select 学生信息,学号,班级,学校, 县 as 县层面排序值, 学校 as 学校排序值, 班级 as 班级排序值, 类型标识(用于区分数据的层次)

       union all

       select 班级小计(count(学号), sum(分数)), null 班级排序值,.............

           group by 县级,学校,班级

       union all

       select 学校合计(count(学号), sum(分数)), null 学校排序值,.............

            group by 县级,学校

       union all

       select 全县合计(count(学号), sum(分数)), null 县层面排序值,.............)

            group by 县级

       order 县层面排序值,学校排序值,班级排序值, 学号

)

当然这些在数据量不大的情况还行, 如果数据量大的话, 各种处理方式, 最好把统计的数据缓存起来, 不要重复执行sum, count 这些耗时的操作.

--------------------------------------可爱的分割线---------------------------------

像上面这样的统计, 还有更简单的做法, 那就是使用 group by rollup(学校, 班级,(学号,姓名)),或者用group by rollup(学校,班级,学号),姓名字段用max(姓名) as 姓名

学校班级学号姓名分数(sum)人数(count)
grouping_id(学校,班级,学号)
学校A班级0101小林9010
学校A班级0102小李6010
学校A班级0201小强75 0
学校B班级0101小小85 0
学校A班级01  15021
学校A班级02  7511
学校B班级01  8511
学校A



225
3
3
学校B



85
1
3
    30547

在前面说过了, oracle 排序中null 默认是最大值, 所以如果是升序, 直接order by  学校, 班级, 学号 就行了, 很方便吧.

grouping_id() 方法是配合group by rollup 使用的, 这里我们用来区分数据层次, 我们可以看到0,1,3,7 这样的数字, 可能不好理解,

但是转为二进制就好理解了, 111 =7   011=3  001=1 000=0,      简单来说, 统计的范围越大, 数字越大,

group by rollup(学校, 班级,(学号,姓名))

 分解成       group by 学校, 班级, (学号,姓名) + group by 学校, 班级 + group by 学校 + group by null

grouping_id(学校,班级,学号)         000          001     011      111

一般思路:

select 调整显示内容

from (

        select 学校, 班级, 学号, 姓名, sum(分数) 分数, count(学号) 学号, grouping_id(学校,班级,学号)  gp_num

        ...................... 

        group by rollup(学校, 班级,(学号,姓名))

        order by  学校, 班级, 学号

)

这种写法跟上面的写法, 效率差距不大, 但是简洁很多, 当然不同的数据库对应null 的排序优先级是不一致的, 用第一种方法通用性强一点, 但是写起来代码很长.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值