学习笔记22/1/10

学习笔记

一、分析函数

聚合函数(分组函数)最后只能看到一个聚合后的结果;
分析函数(开窗函数)既可以看到聚合后的结果还能看到组内的明细;

1、简单运用:

语法:
函数名(列)over(【partition by 列】【order by 列】)

1、min
2、max
3、avg
4、sum
5、count

eg:查询每个部门的最低工资,职位最低工资,又提成的人数;

SELECT DISTINCT E.DEPTNO,MIN(SAL)OVER(PARTITION BY DEPTNO) 最低,
       E.JOB,AVG(SAL)OVER(PARTITION BY JOB) 平均,
			 COUNT(COMM)OVER() 人数
FROM EMP E
ORDER BY E.DEPTNO;

eg:查询员工的姓名,部门编号,经理编号,部门最低工资,经理下属数量,全表最高工资;

SELECT A.ENAME,A.DEPTNO,A.MGR,
       MIN(A.SAL)OVER(PARTITION BY DEPTNO) 最低,
			 COUNT(1)OVER(PARTITION BY MGR) 下属数量,
			 MAX(A.SAL)OVER() 最高
FROM EMP A;

eg:查询SMITH的员工姓名,部门编号,职位,部门最高工资,职位总工资,以及部门的最早的入职日期;

WITH Z AS (SELECT A.ENAME 姓名,
          A.DEPTNO 部门编号,
					A.JOB 职位,
          MAX(A.SAL)OVER(PARTITION BY DEPTNO) 部门最高工资,
			    SUM(A.SAL)OVER(PARTITION BY JOB) 职位总工资,
			    MIN(A.HIREDATE)OVER(PARTITION BY DEPTNO) 部门最早入职日期
          FROM EMP A)

SELECT 姓名,部门编号,部门最高工资,职位总工资,部门最早入职日期
FROM Z
WHERE 姓名 = 'SMITH';

上述示例不能采用如下代码运行:

SELECT A.ENAME 姓名,
          A.DEPTNO 部门编号,
          A.JOB 职位,
          MAX(A.SAL)OVER(PARTITION BY DEPTNO) 部门最高工资,
          SUM(A.SAL)OVER(PARTITION BY JOB) 职位总工资,
          MIN(A.HIREDATE)OVER(PARTITION BY DEPTNO) 部门最早入职日期
          FROM EMP A
					WHERE A.ENAME = 'SMITH';

因为查询语句的运行顺序是先从from语句选中表,然后进行下面的where条件筛选,最后select后选中的数据显示出到窗口上,按照这个执行顺序,下面的那种方法所查询的只有emp表中Smith那一行的数据;

eg:查询20部门的所有工资高于全表平均工资的员工姓名,工资,全表平均工资;

方法一(全连接):

SELECT A.ENAME,A.SAL,平均工资
FROM EMP A
FULL JOIN 
(SELECT AVG(SAL) 平均工资
FROM EMP)
ON A.EMPNO = 平均工资
WHERE A.DEPTNO = 20
AND A.SAL > (SELECT AVG(B.SAL)
             FROM EMP B)
OR A.EMPNO IS NULL;

方法二(分析函数):

WITH Z AS (SELECT A.DEPTNO AD,A.ENAME AN,A.SAL ASA,AVG(A.SAL)OVER() ASAA FROM EMP A)
SELECT *
FROM Z
WHERE AD = 20 AND ASA>ASAA;
对于开窗函数over()的补充研究:

对于开窗函数的理解在学习之后仍感到不太熟悉,使用继续进行了一下研究:

eg:显示各部门员工的工资,并附带显示该部分的最高工资

SELECT SELECT DEPTNO 部门编号,
       ENAME 员工姓名,
       SAL 员工工资,
       MAX(SAL)OVER(PARTITION BY DEPTNO ORDER BY ENAME) 该部门最高工资
FROM EMP;

这是一开始我写的函数,放到emp表中进行测试时,总是发现第一行的“该部门最高工资”总是会显示第一个人的工资数据,并非该部门最高工资,而其他博主的代码运行时却可以正常输出部门最高工资,在思考之后,我将order by语句后的条件改为按照工资降序,就可以正常运行,如下所示:

SELECT DEPTNO 部门编号,
       ENAME 员工姓名,
       SAL 员工工资,
       MAX(SAL)OVER(PARTITION BY DEPTNO ORDER BY SAL DESC) 该部门最高工资
FROM EMP;

个人猜测,开窗函数over()先经过后面的分组排序后,再与一条条的数据进行比对,再对应输出框中留下当时对应的数据,如前面的max()函数就是留下当前的最大值,这也是开窗函数能进行累计的原因吧!
当然,后续还需要验证;

后续研究了一下就是说,上诉两种方法相当于下列代码:

SELECT DEPTNO 部门,
       ENAME 员工姓名,
			 SAL 员工工资,
			 SUM(SAL)OVER(PARTITION BY DEPTNO ORDER BY ENAME
			 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) 累加总和
FROM EMP;   

即在分组后统计到当前行,但第二种方法通过sal的降序排序第一行即为最大值所以就可以得到正确答案;

注:
(1)当sql语句与over()函数中都有order by语句时,over()函数的优先级要比sql语句中的优先级要高,即先执行over()中的order by语句;
(2)标准的over()函数写法,over(分析子句 排序子句 窗口子句),上面以及介绍了前两种子句,这里介绍一下窗口子句,就是用来限定数据范围的子句:
eg:对各部门进行分组,并附带显示第一行至当前行的汇总

SELECT DEPTNO 部门,
       ENAME 员工姓名,
			 SAL 员工工资,
			 SUM(SAL)OVER(PARTITION BY DEPTNO ORDER BY ENAME
			 ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW ) 累加总和
FROM EMP;   

eg:当前行至最后一行的汇总

SELECT DEPTNO 部门,
       ENAME 员工姓名,
			 SAL 员工工资,
			 SUM(SAL)OVER(PARTITION BY DEPTNO ORDER BY ENAME
			 ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING ) 累加总和
FROM EMP; 

eg:当前行的上一行(rownum-1)到当前行的汇总

SELECT DEPTNO 部门,
       ENAME 员工姓名,
			 SAL 员工工资,
			 SUM(SAL)OVER(PARTITION BY DEPTNO ORDER BY ENAME
			 ROWS BETWEEN 1 PRECEDING AND CURRENT ROW ) 累加总和
FROM EMP; 

eg:当前行的上一行(rownum-1)到当前行的下辆行(rownum+2)的汇总

SELECT DEPTNO 部门,
       ENAME 员工姓名,
			 SAL 员工工资,
			 SUM(SAL)OVER(PARTITION BY DEPTNO ORDER BY ENAME
			 ROWS BETWEEN 1 PRECEDING AND 2 FOLLOWING ) 累加总和
FROM EMP; 

以上示例就是窗口子句对于所取数据范围的限定手段;

(3)窗口子句不能单独出现,必须有order by子句出现时才能出现,而出现order by子句的时候,不一定要有窗口子句,但效果会很不一样,此时的窗口默认是当前组的第一行到当前行;
(4)如果同时省略掉窗口子句与分组子句,那么窗口默认为整个分组,再省略掉分组语句就是默认当前表;

以上就是对over()函数的补充研究,上述所提到的相关资料来源于下面的链接:
oracle分析函数技术详解(配上开窗函数over())

2、累计

1.如果求累计值(按值累计),在over的后面加上order by

SELECT SAL,
       SUM(SAL) OVER(ORDER BY SAL) 和值,
			 AVG(SAL) OVER(ORDER BY SAL) 平均值
FROM EMP;

2.按行累计,只需要在上诉order by语句后面加上:
rows between unbounded preceding and current row

SELECT SAL,
       SUM(SAL) OVER(ORDER BY SAL
			 rows between unbounded preceding and current row) 和值,
			 AVG(SAL) OVER(ORDER BY SAL
			 rows between unbounded preceding and current row) 平均值
FROM EMP;

3.按照部门分组,按名字排序,查询员工姓名,部门,部门累计总工资,部门累计平均工资;

SELECT ENAME 员工姓名,
       DEPTNO 部门,
       SUM(SAL)OVER(PARTITION BY DEPTNO ORDER BY ENAME 
			  rows between unbounded preceding and current row) 累计总工资,
			 AVG(SAL)OVER(PARTITION BY DEPTNO ORDER BY ENAME
			  rows between unbounded preceding and current row) 累计平均工资
FROM EMP

3.必须排序的函数

1.row_number()over([ partition by 列]order by 列) --必须排序
不重复 没有并列的情况 1 2 3 4 5

2.rank()over( [ partition by 列 ]order by 列 ) --必须排序
并列跳一级 1 2 3 3 5

3.dense_rank()over( [ partition by 列 ]order by 列 ) --必须排序
并列不跳级 1 2 2 3 3 3 4 5

eg:查询员工姓名,工资,工资排名

SELECT A.ENAME,A.SAL,
       ROW_NUMBER()OVER(ORDER BY SAL DESC) AA,
			 RANK()OVER(ORDER BY SAL DESC)  BB,
			 DENSE_RANK()OVER(ORDER BY SAL DESC) CC
FROM EMP A;

eg:查询员工姓名,工资,部门,以及部门内的工资排名

SELECT ENAME ,
       SAL ,
			 DEPTNO ,
			 RANK()OVER(PARTITION BY DEPTNO ORDER BY SAL DESC) 工资排名
FROM EMP;

eg:查询每个部门工资排名第一的员工信息

WITH Z AS (SELECT EMPNO ENO,
           ENAME ,
           SAL ,
			     DEPTNO ,
			     RANK()OVER(PARTITION BY DEPTNO ORDER BY SAL DESC) 工资排名
           FROM EMP)
					 
SELECT A.*
FROM EMP A
JOIN Z
ON ENO = A.EMPNO
WHERE 工资排名 = 1;
关于ROW_NUMBER()、RANK()、DENSE_RANK()函数的补充研究:

无他,就是上述所说的差不多:
1.row_number():返回的是行信息,没有排名(唯一不重复);
2.dense_rank():返回的相关等级不会跳跃(重复不跳跃);
3.rank():返回的返回的相关等级会跳跃(重复且跳跃);

4.往上提
lead(列【,数【,值】】)over(【partition by 列】order by)
列:要往上提的列
数:向上提几位 不写默认为1
值:填充的值 不写默认是空

lag(列【,数【,值】】)over(【partition by 列】order by)
列:要往下拉的列
数:向下拉几位 不写默认为1
值:填充的值 不写默认是空

注:填充的值要和原来的列中的值类型一致

eg:按照工资降序排列,查询每个人的工资和他下一个人的工资

SELECT SAL,LEAD(SAL)OVER(ORDER BY SAL DESC)
FROM EMP;

eg:按照工资降序,查询每个人比下一个人的工资多多多少

SELECT SAL-NVL(LEAD(SAL)OVER(ORDER BY SAL DESC),0)
FROM EMP;

eg:按照工资降序,查询每个人比上一个人的工资少多少

SELECT NVL(LAG(SAL)OVER(ORDER BY SAL DESC),0) - SAL
FROM EMP;

关于lead()与lag()函数的补充研究:

注:
(1)注意lead()是向上提,就是指将后面指定行的数据提取出来,不要理解成前面几行,lag()函数亦然;
(2)lead(列,数,值)中间的列是指自己指定的列,指定数的多少行,当超出范围时返回括号中的值,lag()亦然;

4.分组连接字符串

1.wm_concat(列)over() 分组连接字符串

eg:查询每个人的姓名,职位,每种职位对应的人名

SELECT ENAME,JOB,wm_concat(ENAME)OVER(PARTITION BY JOB)
FROM EMP;

2.listagg(列,‘分隔符’)within group(order by 列)over() 分组连接字符

eg:查询每个人的姓名,职位,每种职位对应的人名

SELECT ENAME,JOB,
       LISTAGG(ENAME,'-')WITHIN GROUP(ORDER BY ENAME)OVER()
FROM EMP;
这个部分的重点就是以下注意事项

注:两者的区别在与,第一种wm_concat(列)over()函数的分隔符默认为用逗号隔开,但第二种可以指定分隔符以及对生成的数据进行排序;

以上两个函数还可以与分组函数一起使用:
eg:查询每个部门的人的姓名和最大工资

SELECT DEPTNO,WM_CONCAT(ENAME),MAX(SAL)
FROM EMP
GROUP BY DEPTNO;




SELECT DEPTNO,
       LISTAGG(ENAME,'-')WITHIN GROUP(ORDER BY SAL),
			 MAX(SAL)
FROM EMP
GROUP BY DEPTNO;

5.行列转换

建立测试表

代码如下:
CREATE TABLE "DEMO" 
   (  "ID" NUMBER(*,0), 
  "NAME" VARCHAR2(20), 
  "NUMS" NUMBER(*,0)
   ) ;
insert into DEMO (id, name, nums)
values (1, '苹果', 1000);
insert into DEMO (id, name, nums)
values (2, '苹果', 2000);
insert into DEMO (id, name, nums)
values (3, '苹果', 4000);
insert into DEMO (id, name, nums)
values (1, '橘子', 5000);
insert into DEMO (id, name, nums)
values (2, '橘子', 3000);
insert into DEMO (id, name, nums)
values (3, '葡萄', 3500);
insert into DEMO (id, name, nums)
values (4, '芒果', 4200);
insert into DEMO (id, name, nums)
values (4, '芒果', 5500);
commit;

CREATE TABLE "SCOTT"."KECHENG" 
   (  "ID" NUMBER, 
  "NAME" VARCHAR2(20), 
  "COURSE" VARCHAR2(20), 
  "SCORE" NUMBER
   ) ;
insert into KECHENG (id, name, course, score)
values (1, '张三', '语文', 67);
insert into KECHENG (id, name, course, score)
values (1, '张三', '数学', 76);
insert into KECHENG (id, name, course, score)
values (1, '张三', '英语', 43);
insert into KECHENG (id, name, course, score)
values (1, '张三', '历史', 56);
insert into KECHENG (id, name, course, score)
values (1, '张三', '化学', 11);
insert into KECHENG (id, name, course, score)
values (2, '李四', '语文', 54);
insert into KECHENG (id, name, course, score)
values (2, '李四', '数学', 81);
insert into KECHENG (id, name, course, score)
values (2, '李四', '英语', 64);
insert into KECHENG (id, name, course, score)
values (2, '李四', '历史', 93);
insert into KECHENG (id, name, course, score)
values (2, '李四', '化学', 27);
insert into KECHENG (id, name, course, score)
values (3, '王五', '语文', 24);
insert into KECHENG (id, name, course, score)
values (3, '王五', '数学', 25);
insert into KECHENG (id, name, course, score)
values (3, '王五', '英语', 8);
insert into KECHENG (id, name, course, score)
values (3, '王五', '历史', 45);
insert into KECHENG (id, name, course, score)
values (3, '王五', '化学', 1);
commit;


SELECT * FROM DEMO;
SELECT * FROM KECHENG;


create table demo1 as 
SELECT ID,
   SUM(CASE NAME WHEN '苹果' then NUMS END) 苹果,
   SUM(CASE NAME WHEN '橘子' then NUMS END) 橘子,
   SUM(CASE NAME WHEN '葡萄' then NUMS END) 葡萄,
   SUM(CASE NAME WHEN '芒果' then NUMS END) 芒果
from DEMO
GROUP BY ID
ORDER BY ID;

create table demo2 as
SELECT NAME,
   SUM(CASE id WHEN 1 then NUMS END) 一季度,
   SUM(CASE id WHEN 2 then NUMS END) 二季度,
   SUM(CASE id WHEN 3 then NUMS END) 三季度,
   SUM(CASE id WHEN 4 then NUMS END) 四季度
from DEMO
GROUP BY NAME
ORDER BY NAME;

SELECT * FROM DEMO1;
SELECT * FROM DEMO2;

1.行转列

case when/decode + 聚合函数 + group by

eg:把kecheng中的course列行转列

SELECT ID,NAME,
       SUM(CASE COURSE WHEN '语文' THEN SCORE END ) 语文,
			 SUM(CASE COURSE WHEN '数学' THEN SCORE END ) 数学,
			 SUM(CASE COURSE WHEN '英语' THEN SCORE END ) 英语,
			 SUM(CASE COURSE WHEN '历史' THEN SCORE END ) 历史,
			 SUM(CASE COURSE WHEN '化学' THEN SCORE END ) 化学
FROM KECHENG
GROUP BY ID,NAME
ORDER BY ID;

注:当我们需要统计的数据的类型为数字类型时,使用sum函数;
而当我们需要统计的数据类型为字符或者字符串类型时,使用max或者min函数;

eg:把kecheng中的name行转列

SELECT COURSE,
       SUM(CASE NAME WHEN '张三' THEN SCORE END ) 张三,
       SUM(CASE NAME WHEN '李四' THEN SCORE END ) 李四,
       SUM(CASE NAME WHEN '王五' THEN SCORE END ) 王五
FROM KECHENG
GROUP BY COURSE;

eg: 把demo的name列进行行转列

SELECT ID,
       SUM(CASE NAME WHEN '苹果' THEN NUMS END) 苹果,
			 SUM(CASE NAME WHEN '橘子' THEN NUMS END) 橘子,
			 SUM(CASE NAME WHEN '葡萄' THEN NUMS END) 葡萄,
			 SUM(CASE NAME WHEN '芒果' THEN NUMS END) 芒果
FROM DEMO
GROUP BY ID;

eg:把demo的id进行行转列

SELECT NAME,
       SUM(CASE ID WHEN 1 THEN NUMS END) 季度1,
			 SUM(CASE ID WHEN 2 THEN NUMS END) 季度2,
			 SUM(CASE ID WHEN 3 THEN NUMS END) 季度3,
			 SUM(CASE ID WHEN 4 THEN NUMS END) 季度4
FROM DEMO
GROUP BY NAME;

2.列转行

union all

eg:把demo1实现列转行

SELECT * FROM (
SELECT ID,'苹果' NAME,苹果 NUMS
FROM DEMO1
UNION ALL
SELECT ID,'橘子' NAME,橘子 NUMS
FROM DEMO1
UNION ALL
SELECT ID,'葡萄' NAME,葡萄 NUMS
FROM DEMO1
UNION ALL
SELECT ID,'芒果' NAME,芒果 NUMS
FROM DEMO1)
WHERE NUMS IS NOT NULL;

eg:把demo2实现列转行

SELECT * FROM (
SELECT '1' ID,NAME,一季度 NUMS
FROM DEMO2
UNION ALL
SELECT '2' ID,NAME,二季度 NUMS
FROM DEMO2
UNION ALL
SELECT '3' ID,NAME,三季度 NUMS
FROM DEMO2
UNION ALL
SELECT '4' ID,NAME,四季度 NUMS
FROM DEMO2)
WHERE NUMS IS NOT NULL;

3.行列转换函数

行转列 :pivot(被聚合的列 for 行转列的列 in(列中值。。。))

eg:demo的name列行转列

SELECT * FROM DEMO
PIVOT(SUM(NUMS) FOR NAME IN('苹果' 苹果,'橘子' 橘子,
                            '葡萄' 葡萄,'芒果' 芒果));

eg:把kecheng的name列行转列

SELECT * FROM DEMO
PIVOT(SUM(NUMS) FOR NAME IN('苹果' 苹果,'橘子' 橘子,
                            '葡萄' 葡萄,'芒果' 芒果));

列转行 :unpivot(聚合列新列名 for 列转行的新列名 in(字段名。。。))

eg:把demo1列转行

SELECT * FROM DEMO
PIVOT(SUM(NUMS) FOR NAME IN('苹果' 苹果,'橘子' 橘子,
                            '葡萄' 葡萄,'芒果' 芒果));

eg:把demo2列转行

SELECT * FROM DEMO
PIVOT(SUM(NUMS) FOR NAME IN('苹果' 苹果,'橘子' 橘子,
                            '葡萄' 葡萄,'芒果' 芒果));
行列转换函数补充研究:
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值