博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Oracle]高效的SQL语句之分析函数
阅读量:6434 次
发布时间:2019-06-23

本文共 9153 字,大约阅读时间需要 30 分钟。

实际应用中我们可以通过sum()统计出组中的总计或者是累加值,具体示例如下:

1.创建演示表

create
 
table
 emp
as
select
 
*
 
from
 scott.emp;
alter
 
table
 emp
add
 
constraint
 emp_pk
primary
 
key
(empno);
create
 
table
 dept
as
select
 
*
 
from
 scott.dept;
alter
 
table
 dept
add
 
constraint
 dept_pk
primary
 
key
(deptno);

2. sum()语句如下:

select
 deptno,
       ename,
       sal,
  
--
按照部门薪水累加(order by改变了分析函数的作用,只工作在当前行和前一行,而不是所有行)
       
sum
(sal) 
over
 (partition 
by
 deptno 
order
 
by
 sal) CumDeptTot,  
       
sum
(sal) 
over
 (partition 
by
 deptno) SalByDept,  
--
统计一个部门的薪水
       
sum
(sal) 
over
 (
order
 
by
 deptno,sal) CumTot,  
--
所有雇员的薪水一行一行的累加
       
sum
(sal) 
over
 () TotSal  
--
统计总薪水
  
from
 emp
 
order
 
by
 deptno, sal

3. 结果如下:

10    MILLER    1300.00    1300    8750    1300    29025
10    CLARK    2450.00    3750    8750    3750    29025
10    KING    5000.00    8750    8750    8750    29025
20    SMITH    800.00    800    10875    9550    29025
20    ADAMS    1100.00    1900    10875    10650    29025
20    JONES    2975.00    4875    10875    13625    29025
20    SCOTT    3000.00    10875    10875    19625    29025
20    FORD    3000.00    10875    10875    19625    29025
30    JAMES    950.00    950    9400    20575    29025
30    WARD    1250.00    3450    9400    23075    29025
30    MARTIN    1250.00    3450    9400    23075    29025
30    TURNER    1500.00    4950    9400    24575    29025
30    ALLEN    1600.00    6550    9400    26175    29025
30    BLAKE    2850.00    9400    9400    29025    29025

 

如果我们按照示例想得到每个部门薪水值最高的雇员的纪录,可以有四种方法实现:

先创建示例表

create
 
table
 emp
as
select
 
*
 
from
 scott.emp;
alter
 
table
 emp
add
 
constraint
 emp_pk
primary
 
key
(empno);
create
 
table
 dept
as
select
 
*
 
from
 scott.dept;
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;
alter
 
table
 emp
add
 
constraint
 emp_pk
primary
 
key
(empno);
create
 
table
 dept
as
select
 
*
 
from
 scott.dept;
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
  );
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
        

 

Oracle支持通过ref游标在调用存储过程后返回结果集, 使用游标在内存消耗以及时间上都要大大的优于返回数组变量的做法!

示例如下:数据库方面,建立一个Package

create
 
or
 
replace
 package ref_cur_demo 
is
 type rc 
is
 ref 
cursor
;
 
procedure
 ref_cursor(p_owner 
in
 
varchar2
,p_cursor 
in
 out rc);
end
 ref_cur_demo;
create
 
or
 
replace
 package body ref_cur_demo 
is
 
procedure
 ref_cursor(p_owner 
in
 
varchar2
,p_cursor 
in
 out rc)
 
is
 
 
begin
      
open
 p_cursor 
for
 
select
 
object_name
,object_type 
from
 all_objects 
where
 owner 
=
 p_owner 
and
 rownum
<
3
 
end
;
  
end
 ref_cur_demo;

程序方面使用C#建立一个小型应用程序,主要代码如下:

            Oracle.DataAccess.Client.OracleConnection oracleConnection1
=
new
 OracleConnection(
"
data source=precolm2;user id=colmtest;password=colmtest
"
);
            oracleConnection1.Open();
            
string
 strSQL 
=
 
@"
ref_cur_demo.ref_cursor
"
;
            Oracle.DataAccess.Client.OracleDataAdapter da 
=
 
new
 Oracle.DataAccess.Client.OracleDataAdapter();
            Oracle.DataAccess.Client.OracleCommand cmd 
=
 
new
 Oracle.DataAccess.Client.OracleCommand(strSQL,oracleConnection1);
            cmd.CommandType 
=
 CommandType.StoredProcedure;
            Oracle.DataAccess.Client.OracleParameter pram 
=
 
new
 Oracle.DataAccess.Client.OracleParameter(
"
p_owner
"
,Oracle.DataAccess.Client.OracleDbType.Varchar2);
            pram.Value 
=
 
"
COLMTEST
"
;
            cmd.Parameters.Add(pram);
            Oracle.DataAccess.Client.OracleParameter pram1 
=
 
new
 Oracle.DataAccess.Client.OracleParameter(
"
p_cursor
"
,Oracle.DataAccess.Client.OracleDbType.RefCursor);
            pram1.Direction 
=
 ParameterDirection.Output;
            cmd.Parameters.Add(pram1);
            
            da.SelectCommand 
=
 cmd;
            DataSet ds 
=
 
new
 DataSet();
            da.Fill(ds);
                
            
this
.dataGrid1.DataSource
=
ds.Tables[
0
].DefaultView;

 

分类: 
本文转自快乐就好博客园博客,原文链接:http://www.cnblogs.com/happyday56/archive/2007/06/26/795859.html,如需转载请自行联系原作者
你可能感兴趣的文章
postgres函数
查看>>
Microsoft AJAX Library Cheat Sheet(5): Number和Error类型的扩展
查看>>
AfxGetMainWnd函数
查看>>
WebView增加一个水平Progress,位置、长相随意
查看>>
easyui messager alert 三秒后自动关闭提示
查看>>
core data 基础操作
查看>>
ORM框架Hibernate (四) 一对一单向、双向关联映射
查看>>
offsetLeft, offsetTop以及postion().left , postion().top有神马区别
查看>>
数据库中触发器before与after认识
查看>>
手动露天广场和立方体
查看>>
随机选择
查看>>
【Java并发编程三】闭锁
查看>>
分布式事务中遇到的 “与基础事务管理器的通信失败”的解决方法
查看>>
让你的Git水平更上一层楼的10个小贴士
查看>>
c++ string 之 find_first_not_of 源码
查看>>
mybatis中的#和$的区别
查看>>
ubuntu下搭建NDK环境
查看>>
MessageDigest简单介绍
查看>>
webpack window 使用sass来编译css样式
查看>>
D3 & Data Visualization in Ext JS
查看>>