转自:https://blog.csdn.net/weixin_39654352/article/details/111248720
上次主要介绍达梦数据库的常用集函数和分析函数,这次在上次的基础上继续介绍相邻函数 LAG 和 LEAD,占比函数RATIO_TO_REPORT等分析函数,并补充介绍分组函数GROUP BY、ROLLUP、CUBE的用法以及区别。
本章的测试环境:
操作系统: 中标麒麟6 64位
数据库版本:达梦8.1
达梦数据库客户端:DM管理工具
分析函数
本次重点讲解相邻函数 LAG 和 LEAD,占比函数RATIO_TO_REPORT两个分析函数。
● 相邻函数LAG和LEAD ●
LAG和LEAD函数是跟偏移量相关的两个分析函数,通过这两个函数可以在一次查询中取出同一字段的前N行的数据(lag)和后N行的数据(lead)作为独立的列,从而更方便地进行数据筛选。这种操作可以代替表的自联接,不过LAG和LEAD有更高的效率。
语法:
LAG(EXP_STR,OFFSET,DEFVAL)OVER()LEAD(EXP_STR,OFFSET,DEFVAL)OVER()EXP_STR:要取的列OFFSET:取偏移后的第几行数据DEFVAL:没有符合条件的默认值
①比如获取某公司各员工薪资情况,并同时展示同部门比该员工高1级的员工薪资,同部门比该员工低1级的员工薪资,总公司比该员工高1级的员工薪资情况,总公司比该员工低1级的薪资情况,sql样例参考如下:
select dept.department_name, emp.employee_name, emp.salary, lag(salary)over(partition by dept.department_name order by salary desc) dept_lag, lead(salary)over(partition by dept.department_name order by salary desc) dept_lead, lag(salary)over(order by salary desc) total_lag, lead(salary)over(order by salary desc) total_lead from employees emp, department dept where emp.department_id = dept.department_id(+);
输出结果展示如下(数值列依次为该部门员工薪资,同部门比该员工高1级的员工薪资,同部门比该员工低1级的员工薪资,总公司内比该员工高1级的员工薪资情况,总公司内比该员工低1级的薪资情况):
相邻函数也常用于公司年终财务报表计算同比和环比的场景。
②比如获取销售的业绩情况,同比分析相同月份上年和下年的情况,环比分析上月和下月的销售情况,sql样例参考如下:
select to_char(trunc(logtime, 'mm'),'yyyy-mm-dd') "月份", to_char(sum(price),'fm999990.00') "当月收入", -- 环比分析,与上个月份进行比较 to_char(lag(sum(price), 1) over(order by trunc(logtime, 'mm')), 'fm999990.00') as "环比上月", -- 环比分析,与下个月份进行比较 to_char(lead(sum(price), 1) over(order by trunc(logtime, 'mm')),'fm999990.00') as "环比下月", -- 同比分析,与上个年度相同月份进行比较 to_char(lag(sum(price), 12) over(order by trunc(logtime, 'mm')), 'fm999990.00') as "同比上年", -- 同比分析,与下个年度相同月份进行比较 to_char(lead(sum(price), 12) over(order by trunc(logtime, 'mm')),'fm999990.00') as "同比下年" from orderproduct group by trunc(logtime, 'mm') order by 1 desc;
输出结果展示如下(数值列依次为当月收入,环比上月收入,环比下月收入,同比上年收入,同比下年收入):
● 占比函数RATIO_TO_REPORT ●
占比函数常用于财务中计算收支占比。用于取某个值占总和的百分比。
语法:
RATIO_TO_REPORT(EXP_STR)OVER()RATIO_TO_REPORT() 括号中表达式EXP_STR就是分子,OVER() 括号中就是分母,分母缺省就是整个占比。
①比如获取某公司每个城市员工人数分布和薪资待遇分布情况,sql样例参考如下:
select c.city_name, count(*) toal_personnum, round((ratio_to_report(count(*))over())*100, 2) person_ratio, sum(emp.salary) total_salary, round((ratio_to_report(sum(emp.salary))over())*100, 2) sal_ratio from employee emp, department dept, location l, city c where emp.department_id = dept.department_id(+) and dept.location_id = l.location_id(+) and l.city_id = c.city_id(+) group by c.city_name;
从输出结果中可以看出各个区域公司人员占用百分比,各区域薪资占用百分比;如下结果集可以作为报表饼图的原型。输出结果展示如下:
RATIO_TO_REPORT 可以结合partition by 使用。
②比如获取公司每个员工薪资及员工薪资在所在部门的薪资占比,每个部门总薪资及部门薪资在公司的占比,sql样例参考如下:
select department_name, employee_name, salary, g1, sum(salary) over(partition by decode(g1, 0, department_name, null), g1) sum_salary, ratio_to_report(salary) over(partition by decode(g1, 0, department_name, null), g1) salaryrate from ( select dept.department_name, emp.employee_name, sum(emp.salary) salary, grouping(dept.department_name) + grouping(emp.employee_name) g1 from employees emp, department dept where emp.department_id = dept.department_id group by rollup(dept.department_name, emp.employee_name))
输出结果展示如下(数值列依次员工个人薪资/部门总薪资,分组值,公司薪资合计/部门薪资合计,薪资占比):
从上面的例子中我们看到了group by rollup这个语句,这里也顺带讲解下分组函数group by子句的使用方法。
分组函数GROUP BY子句
GROUP BY 子句是 SELECT 语句的可选项部分。
它定义了分组表。
GROUP BY 子句语法如下:
GROUP BY 子句
ROLLUP 项
CUBE 项
GROUPING SETS 项
GROUP 项 ( )
HAVING 子句
这里介绍ROLLUP、CUBE、GROUPING SETS三项的用法和差异。
用法group by [rollup|cube|grouping sets](colomn)
要弄明白rollup,cube和grouping sets,就要知道group by的使用场景, group by 为对列进行分组,只展现分组统计的值,而rollup 为分层次展现,cube为展现列中所有层次,grouping sets只展现列中单一层次。三者都是group by子句的扩展。
ROLLUP:可以为每个分组返回小计记录以及为所有分组返回总计记录。ROLLUP 生成的结果集显示了所选列中值的某一层次结构的聚合。
CUBE:可以返回每一个列组合的小计记录,同时在末尾加上总计记录。CUBE 生成的结果集显示了所选列中值的所有组合的聚合。
GROUPING SETS:可以返回每一个列的小计记录。GROUPING SETS生成的结果集显示了所选列中值的单一的聚合。
例如:group by rollup(a, b, c) 首先会对(a, b, c)进行group by,然后对(a, b)进行group by,然后是(a)进行group by,最后对全表进行group by操作。
group by cube(a, b, c),首先会对(a, b, c)进行group by,然后依次是(a, b),(a, c),(a),(b, c),(b),(c),最后对全表进行group by操作。
grouping sets (a, b, c),首先会对(a)进行group by,然后依次是(b),(c),不对全表进行group by操作。
具体区别可以查看如下样例。
● ROLLUP ●
比如获取公司各城市每个部门员工人数分布和薪资分布情况,使用group by rollup的sql样例参考如下:
select c.city_name, dept.department_name, count(*) toal_personnum, round((ratio_to_report(count(*))over())*100, 2) person_ratio, sum(emp.salary) total_salary, round((ratio_to_report(sum(emp.salary))over())*100, 2) sal_ratio from employee emp, department dept, location l, city c where emp.department_id = dept.department_id(+) and dept.location_id = l.location_id(+) and l.city_id = c.city_id(+) group by rollup (c.city_name, dept.department_name);
从结果集中可以看出group by rollup聚合了城市和部门组合,城市组合,所有组合三种情况。输出结果展示如下:
● CUBE ●
上例中的sql将rollup换成cube,sql样例参考如下:
select c.city_name, dept.department_name, count(*) toal_personnum, round((ratio_to_report(count(*))over())*100, 2) person_ratio, sum(emp.salary) total_salary, round((ratio_to_report(sum(emp.salary))over())*100, 2) sal_ratio from employee emp, department dept, location l, city c where emp.department_id = dept.department_id(+) and dept.location_id = l.location_id(+) and l.city_id = c.city_id(+) group by cube (c.city_name, dept.department_name);
从结果集中可以看出group by cube聚合了城市和部门组合,城市组合,部门组合,所有组合四种情况。结果集展示如下:
● GROUPING SETS ●
将上例中sql的cube换成grouping sets,sql样例参考如下:
select c.city_name, dept.department_name, count(*) toal_personnum, round((ratio_to_report(count(*))over())*100, 2) person_ratio, sum(emp.salary) total_salary, round((ratio_to_report(sum(emp.salary))over())*100, 2) sal_ratio from employee emp, department dept, location l, city c where emp.department_id = dept.department_id(+) and dept.location_id = l.location_id(+) and l.city_id = c.city_id(+) group by grouping sets (c.city_name, dept.department_name);
从结果集中可以看出grouping sets聚合了城市组合,部门组合两种情况,没有进行组合分组合计。输出结果展示如下: