3. 结果如下:
如果我们按照示例想得到每个部门薪水值最高的雇员的纪录,可以有四种方法实现:
先创建示例表
create table emp
as
select * from scott.emp;
![Oracle]高效的SQL语句之分析函数 Oracle]高效的SQL语句之分析函数](/default/index/img?u=L2RlZmF1bHQvaW5kZXgvaW1nP3U9YUhSMGNEb3ZMMmx0WVdkbGN5NWpjMlJ1TG01bGRDOXplVzUwWVhob2FXZG9iR2xuYUhScGJtY3ZUM1YwYkdsdWFXNW5TVzVrYVdOaGRHOXljeTlPYjI1bExtZHBaZz09)
alter table emp
add constraint emp_pk
primary key(empno);
![Oracle]高效的SQL语句之分析函数 Oracle]高效的SQL语句之分析函数](/default/index/img?u=L2RlZmF1bHQvaW5kZXgvaW1nP3U9YUhSMGNEb3ZMMmx0WVdkbGN5NWpjMlJ1TG01bGRDOXplVzUwWVhob2FXZG9iR2xuYUhScGJtY3ZUM1YwYkdsdWFXNW5TVzVrYVdOaGRHOXljeTlPYjI1bExtZHBaZz09)
create table dept
as
select * from scott.dept;
![Oracle]高效的SQL语句之分析函数 Oracle]高效的SQL语句之分析函数](/default/index/img?u=L2RlZmF1bHQvaW5kZXgvaW1nP3U9YUhSMGNEb3ZMMmx0WVdkbGN5NWpjMlJ1TG01bGRDOXplVzUwWVhob2FXZG9iR2xuYUhScGJtY3ZUM1YwYkdsdWFXNW5TVzVrYVdOaGRHOXljeTlPYjI1bExtZHBaZz09)
alter table dept
add constraint dept_pk
primary key(deptno);
方法1.emp中的每一行都会进行max比较,费时
select * from emp emp1 where emp1.sal=(select max(emp2.sal) from emp emp2 where emp2.deptno=emp1.deptno)
方法2.先子查询查找出max sal,然后与emp表相关联,如果逻辑复杂会产生较多代码
select * from emp emp1,(select deptno,max(sal) maxsal from emp emp2 group by emp2.deptno) emp3 where emp1.deptno=emp3.deptno and emp1.sal=emp3.maxsal
方法3.使用max分析函数
select deptno,maxsal,empno from(
select max(sal) over (partition by deptno) maxsal,emp.* from emp) emp2
where emp2.sal=emp2.maxsal
方法4.使用dense_rank分析函数,如果一个部门可能存在多笔最大薪水,就不能使用row_number()分析函数
select deptno,sal,empno from(
select emp.*,DENSE_RANK() over (partition by deptno order by sal desc) rownumber from emp) emp2
where rownumber=1
结果如下:
10 5000.00 7839
20 3000.00 7788
20 3000.00 7902
30 2850.00 7698
有些时候我们希望得到指定数据中的前n列,示例如下:
得到每个部门薪水最高的三个雇员:
先创建示例表
create table emp
as
select * from scott.emp;
![Oracle]高效的SQL语句之分析函数 Oracle]高效的SQL语句之分析函数](/default/index/img?u=L2RlZmF1bHQvaW5kZXgvaW1nP3U9YUhSMGNEb3ZMMmx0WVdkbGN5NWpjMlJ1TG01bGRDOXplVzUwWVhob2FXZG9iR2xuYUhScGJtY3ZUM1YwYkdsdWFXNW5TVzVrYVdOaGRHOXljeTlPYjI1bExtZHBaZz09)
alter table emp
add constraint emp_pk
primary key(empno);
![Oracle]高效的SQL语句之分析函数 Oracle]高效的SQL语句之分析函数](/default/index/img?u=L2RlZmF1bHQvaW5kZXgvaW1nP3U9YUhSMGNEb3ZMMmx0WVdkbGN5NWpjMlJ1TG01bGRDOXplVzUwWVhob2FXZG9iR2xuYUhScGJtY3ZUM1YwYkdsdWFXNW5TVzVrYVdOaGRHOXljeTlPYjI1bExtZHBaZz09)
create table dept
as
select * from scott.dept;
![Oracle]高效的SQL语句之分析函数 Oracle]高效的SQL语句之分析函数](/default/index/img?u=L2RlZmF1bHQvaW5kZXgvaW1nP3U9YUhSMGNEb3ZMMmx0WVdkbGN5NWpjMlJ1TG01bGRDOXplVzUwWVhob2FXZG9iR2xuYUhScGJtY3ZUM1YwYkdsdWFXNW5TVzVrYVdOaGRHOXljeTlPYjI1bExtZHBaZz09)
alter table dept
add constraint dept_pk
primary key(deptno);
先看一下row_number() /rank()/dense_rank()三个函数之间的区别
select emp.deptno,emp.sal,emp.empno,row_number() over (partition by deptno order by sal desc) row_number, --1,2,3
rank() over (partition by deptno order by sal desc) rank, --1,1,3
dense_rank() over (partition by deptno order by sal desc) dense_rank from emp --1,1,2
结果如下:
10 5000.00 7839 1 1 1
10 2450.00 7782 2 2 2
10 1300.00 7934 3 3 3
20 3000.00 7788 1 1 1
20 3000.00 7902 2 1 1
20 2975.00 7566 3 3 2
20 1100.00 7876 4 4 3
20 800.00 7369 5 5 4
30 2850.00 7698 1 1 1
30 1600.00 7499 2 2 2
取每个部门的薪水前三位雇员:
select t.deptno,t.rank,t.sal from
(
select emp.*,row_number() over (partition by deptno order by sal desc) row_number, --1,2,3
rank() over (partition by deptno order by sal desc) rank, --1,1,3
dense_rank() over (partition by deptno order by sal desc) dense_rank from emp --1,1,2
) t
where t.rank<=3
结果如下:
10 1 5000.00
10 2 2450.00
10 3 1300.00
20 1 3000.00
20 1 3000.00
20 3 2975.00
30 1 2850.00
30 2 1600.00
30 3 1500.00
如果想输出成deptno sal1 sal2 sal3这种类型的格式
步骤一(decode):
select t.deptno,decode(row_number,1,sal) sal1,decode(row_number,2,sal) sal2,decode(row_number,3,sal) sal3 from
(
select emp.*,row_number() over (partition by deptno order by sal desc) row_number, --1,2,3
rank() over (partition by deptno order by sal desc) rank, --1,1,3
dense_rank() over (partition by deptno order by sal desc) dense_rank from emp --1,1,2
) t
where t.rank<=3
结果如下:
10 5000
10 2450
10 1300
20 3000
20 3000
20 2975
30 2850
30 1600
30 1500
步骤二(使用聚合函数去除null,得到最终结果):
select t.deptno,max(decode(row_number,1,sal)) sal1,max(decode(row_number,2,sal)) sal2,max(decode(row_number,3,sal)) sal3 from
(
select emp.*,row_number() over (partition by deptno order by sal desc) row_number, --1,2,3
rank() over (partition by deptno order by sal desc) rank, --1,1,3
dense_rank() over (partition by deptno order by sal desc) dense_rank from emp --1,1,2
) t
where t.rank<=3
group by t.deptno
结果如下:
10 5000 2450 1300
20 3000 3000 2975
30 2850 1600 1500
有时候报表上面需要显示该笔操作的上一步骤或者下一步骤的详细信息,这个时候可以按照下面的做法:
先创建示例表:
-- Create table
create table LEAD_TABLE
(
CASEID VARCHAR2(10),
STEPID VARCHAR2(10),
ACTIONDATE DATE
)
tablespace COLM_DATA
pctfree 10
initrans 1
maxtrans 255
storage
(
initial 64K
minextents 1
maxextents unlimited
);
![Oracle]高效的SQL语句之分析函数 Oracle]高效的SQL语句之分析函数](/default/index/img?u=L2RlZmF1bHQvaW5kZXgvaW1nP3U9YUhSMGNEb3ZMMmx0WVdkbGN5NWpjMlJ1TG01bGRDOXplVzUwWVhob2FXZG9iR2xuYUhScGJtY3ZUM1YwYkdsdWFXNW5TVzVrYVdOaGRHOXljeTlPYjI1bExtZHBaZz09)
insert into LEAD_TABLE values('Case1','Step1',to_date('20070101','yyyy-mm-dd'));
insert into LEAD_TABLE values('Case1','Step2',to_date('20070102','yyyy-mm-dd'));
insert into LEAD_TABLE values('Case1','Step3',to_date('20070103','yyyy-mm-dd'));
insert into LEAD_TABLE values('Case1','Step4',to_date('20070104','yyyy-mm-dd'));
insert into LEAD_TABLE values('Case1','Step5',to_date('20070105','yyyy-mm-dd'));
insert into LEAD_TABLE values('Case1','Step4',to_date('20070106','yyyy-mm-dd'));
insert into LEAD_TABLE values('Case1','Step6',to_date('20070101','yyyy-mm-dd'));
insert into LEAD_TABLE values('Case1','Step1',to_date('20070201','yyyy-mm-dd'));
insert into LEAD_TABLE values('Case2','Step2',to_date('20070202','yyyy-mm-dd'));
insert into LEAD_TABLE values('Case2','Step3',to_date('20070203','yyyy-mm-dd'));
commit;
每一条记录都能连接到上/下一行的内容
select caseid,stepid,actiondate,lead(stepid) over (partition by caseid order by actiondate) nextstepid,
lead(actiondate) over (partition by caseid order by actiondate) nextactiondate,
lag(stepid) over (partition by caseid order by actiondate) prestepid,
lag(actiondate) over (partition by caseid order by actiondate) preactiondate
from lead_table
结果如下:
Case1 Step1 2007-1-1 Step2 2007-1-2
Case1 Step2 2007-1-2 Step3 2007-1-3 Step1 2007-1-1
Case1 Step3 2007-1-3 Step4 2007-1-4 Step2 2007-1-2
Case1 Step4 2007-1-4 Step5 2007-1-5 Step3 2007-1-3
Case1 Step5 2007-1-5 Step4 2007-1-6 Step4 2007-1-4
Case1 Step4 2007-1-6 Step6 2007-1-7 Step5 2007-1-5
Case1 Step6 2007-1-7 Step4 2007-1-6
Case2 Step1 2007-2-1 Step2 2007-2-2
Case2 Step2 2007-2-2 Step3 2007-2-3 Step1 2007-2-1
Case2 Step3 2007-2-3 Step2 2007-2-2
还可以进一步统计一下两者的相差天数
select caseid,stepid,actiondate,nextactiondate,nextactiondate-actiondate datebetween from (
select caseid,stepid,actiondate,lead(stepid) over (partition by caseid order by actiondate) nextstepid,
lead(actiondate) over (partition by caseid order by actiondate) nextactiondate,
lag(stepid) over (partition by caseid order by actiondate) prestepid,
lag(actiondate) over (partition by caseid order by actiondate) preactiondate
from lead_table)
结果如下:
Case1 Step1 2007-1-1 2007-1-2 1
Case1 Step2 2007-1-2 2007-1-3 1
Case1 Step3 2007-1-3 2007-1-4 1
Case1 Step4 2007-1-4 2007-1-5 1
Case1 Step5 2007-1-5 2007-1-6 1
Case1 Step4 2007-1-6 2007-1-7 1
Case1 Step6 2007-1-7
Case2 Step1 2007-2-1 2007-2-2 1
Case2 Step2 2007-2-2 2007-2-3 1
Case2 Step3 2007-2-3