利用Oracle分析函数实现多行数据合并为一行

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了利用Oracle分析函数实现多行数据合并为一行相关的知识,希望对你有一定的参考价值。

参考技术A

  demo场景 以oracle自带库中的表emp为例

  select ename deptno from emp order by deptno;

   ENAME DEPTNO CLARK KING MILLER SMITH ADAMS FORD SCOTT JONES ALLEN BLAKE MARTIN JAMES TURNER WARD

  现在想要将同一部门的人给合并成一行记录 如何做呢?如下

   ENAME DEPTNO CLARK KING MILLER ADAMS FORD JONES SCOTT SMITH ALLEN BLAKE JAMES MARTIN TURNER WARD

  通常我们都是自己写函数或在程序中处理 这里我们利用oracle自带的分析函数row_number()和sys_connect_by_path来进行sql语句层面的多行到单行的合并 并且效率会非常高

  基本思路

   对deptno进行row_number()按ename排位并打上排位号

  select deptno ename row_number() over(partition by deptno order by deptno ename) rank

  from emp order by deptno ename;

   DEPTNO ENAME RANK CLARK KING MILLER ADAMS FORD JONES SCOTT SMITH ALLEN BLAKE JAMES MARTIN TURNER WARD   可看出 经过row_number()后 部门人已经按部门和人名进行了排序 并打上了一个位置字段rank

   利用oracle的递归查询connect by进行表内递归 并通过sys_connect_by_path进行父子数据追溯串的构造 这里要针对ename字段进行构造 使之合并在一个字段内(数据很多 只截取部分)

  select deptno ename rank level as curr_level

  ltrim(sys_connect_by_path(ename ) ) ename_path from (

  select deptno ename row_number() over(partition by deptno order by deptno ename) rank

  from emp order by deptno ename) connect by deptno = prior deptno and rank = prior rank;

  各部门递归后的数据量都是 ( +n)/ * n 即 deptno= 数据量 ( + )/ * = ;

  deptno= 数据量 ( + )/ * = ;      deptno= 数据量 ( + )/ * = ;

   DEPTNO ENAME RANK CURR_LEVEL ENAME_PATH CLARK CLARK KING CLARK KING MILLER CLARK KING MILLER KING KING MILLER KING MILLER MILLER MILLER

   DEPTNO ENAME RANK CURR_LEVEL ENAME_PATH ADAMS ADAMS FORD ADAMS FORD JONES ADAMS FORD JONES SCOTT ADAMS FORD JONES SCOTT SMITH ADAMS FORD JONES SCOTT SMITH FORD FORD JONES FORD JONES SCOTT FORD JONES SCOTT SMITH FORD JONES SCOTT SMITH JONES JONES SCOTT JONES SCOTT SMITH JONES SCOTT SMITH SCOTT SCOTT SMITH SCOTT SMITH SMITH SMITH

  这里我们仅列出deptno= 的 至此我们应该能否发现一些线索了 即每个部门中 curr_level最高的那行 有我们所需要的数据 那后面该怎么办 取出那个数据? 对了 继续用row_number()进行排位标记 然后再按排位标记取出即可

   对deptno继续进行row_number()按curr_level排位

  select deptno ename_path row_number() over(partition by deptno order by deptno curr_level desc) ename_path_rank from (select deptno ename rank level as curr_level

  ltrim(sys_connect_by_path(ename ) ) ename_path from (

  select deptno ename row_number() over(partition by deptno order by deptno ename) rank

  from emp order by deptno ename) connect by deptno = prior deptno and rank = prior rank);

   DEPTNO ENAME_PATH ENAME_PATH_RANK CLARK KING MILLER CLARK KING KING MILLER CLARK KING MILLER DEPTNO ENAME_PATH ENAME_PATH_RANK ADAMS FORD JONES SCOTT SMITH ADAMS FORD JONES SCOTT FORD JONES SCOTT SMITH ADAMS FORD JONES FORD JONES SCOTT JONES SCOTT SMITH ADAMS FORD FORD JONES SCOTT SMITH JONES SCOTT ADAMS JONES SMITH SCOTT FORD   这里还是仅列出deptno为 的 至此应该很明了了 在进行一次查询 取ename_path_rank为 的即可获得我们想要的结果

   获取想要排位的数据 即得部门下所有人多行到单行的合并

  select deptno ename_path from (select deptno ename_path

  row_number() over(partition by deptno order by deptno curr_level desc) ename_path_rank

  from (select deptno ename rank level as curr_level

  ltrim(sys_connect_by_path(ename ) ) ename_path from (

  select deptno ename row_number() over(partition by deptno order by deptno ename) rank

  from emp order by deptno ename) connect by deptno = prior deptno and rank = prior rank))

lishixinzhi/Article/program/Oracle/201311/17343

oracle分析函数

 

一 分析函数

是oracle为解决复杂报表统计的函数,可在数据中分组,并计算基于组的某种统计值。每一组的每一行可以返回一个统计值。

二 分析函数和聚合函数的不同之处

普通聚合函数用group by分组,每个分组返回一个统计值。 分析函数用partition by分组,每组每行可返回一个统计值。

分析函数用于计算基于组的某种聚合值,它和聚合函数的不同之处是,对于每个组返回多行,而聚合函数对于每个组只返回一行。 

三 分析函数的形式

分析函数带有一个开窗函数over() ,包含4个分析子句 : partittion by , order by ,rows , range 。使用形式over(partition by xxx order by yyy rows between zzz  range between *** preceding and *** following)

开窗函数指定了分析函数工作的数据窗口大小,这个数据窗口大小可能随行变化而变化。举例如下:

3.1 

 over(order by salary) 按照salary排序进行累计,order by是个默认的开窗函数
 over(partition by deptno)按照部门分区

3.2 

over(order by salary range between 5 preceding and 5 following)
 每行对应的数据窗口是之前行幅度值不超过5,之后行幅度值不超过5

例如:对于以下列
     aa
     1
     2
     2
     2
     3
     4
     5
     6
     7
     9

sum(aa)overorder by aa range between 2 preceding and 2 following)

 得出的结果是
            AA                       SUM
            ---------------------- ------------------------------------------------------- 
            1                       10                                                      
            2                       14                                                      
            2                       14                                                      
            2                       14                                                      
            3                       18                                                      
            4                       18                                                      
            5                       22                                                      
            6                       18                                                                
            7                       22                                                                
            9                       9                 

 就是说,对于aa=5的一行 ,sum为   5-1<=aa<=5+2 的和
   对于aa=2来说 ,sum=1+2+2+2+3+4=14     ;
   又如 对于aa=99-1<=aa<=9+2 只有9一个数,所以sum=9

3.3

over(order by salary rows between 2 preceding and 4 following)
每行对应的数据窗口是之前2行,之后4行 

下面三条语句等效:           
     overorder by salary rows between unbounded preceding and unbounded following)
          每行对应的数据窗口是从第一行到最后一行,等效:
     overorder by salary range between unbounded preceding and unbounded following)
           等效
     over(partition by null)

参考:https://blog.csdn.net/zs064811/article/details/51979836

四 样例(在scott用户下模拟)

【在oracle livesql中测试,livesql.oracle.com

   

 执行对应的sql脚本作为测试数据。

sql清单:

-- 创建部门表
create table dept(  
  deptno     number(2,0),  
  dname      varchar2(14),  
  loc        varchar2(13),  
  constraint pk_dept primary key (deptno)  
);

--创建emp表
create table emp(  
  empno    number(4,0),  
  ename    varchar2(10),  
  job      varchar2(9),  
  mgr      number(4,0),  
  hiredate date,  
  sal      number(7,2),  
  comm     number(7,2),  
  deptno   number(2,0),  
  constraint pk_emp primary key (empno),  
  constraint fk_deptno foreign key (deptno) references dept (deptno)  
);

-- 插入dept数据
insert into DEPT (DEPTNO, DNAME, LOC)
values(10, \'ACCOUNTING\', \'NEW YORK\');
insert into dept  
values(20, \'RESEARCH\', \'DALLAS\');
insert into dept  
values(30, \'SALES\', \'CHICAGO\');
insert into dept  
values(40, \'OPERATIONS\', \'BOSTON\');
-- 插入emp数据
insert into emp  
values(  
 7839, \'KING\', \'PRESIDENT\', null,  
 to_date(\'17-11-1981\',\'dd-mm-yyyy\'),  
 5000, null, 10  
);

insert into emp  
values(  
 7698, \'BLAKE\', \'MANAGER\', 7839,  
 to_date(\'1-5-1981\',\'dd-mm-yyyy\'),  
 2850, null, 30  
);
insert into emp  
values(  
 7782, \'CLARK\', \'MANAGER\', 7839,  
 to_date(\'9-6-1981\',\'dd-mm-yyyy\'),  
 2450, null, 10  
);
insert into emp  
values(  
 7566, \'JONES\', \'MANAGER\', 7839,  
 to_date(\'2-4-1981\',\'dd-mm-yyyy\'),  
 2975, null, 20  
);
insert into emp  
values(  
 7788, \'SCOTT\', \'ANALYST\', 7566,  
 to_date(\'13-JUL-87\',\'dd-mm-rr\') - 85,  
 3000, null, 20  
);
insert into emp  
values(  
 7902, \'FORD\', \'ANALYST\', 7566,  
 to_date(\'3-12-1981\',\'dd-mm-yyyy\'),  
 3000, null, 20  
);
insert into emp  
values(  
 7369, \'SMITH\', \'CLERK\', 7902,  
 to_date(\'17-12-1980\',\'dd-mm-yyyy\'),  
 800, null, 20  
);
insert into emp  
values(  
 7499, \'ALLEN\', \'SALESMAN\', 7698,  
 to_date(\'20-2-1981\',\'dd-mm-yyyy\'),  
 1600, 300, 30  
);
insert into emp  
values(  
 7521, \'WARD\', \'SALESMAN\', 7698,  
 to_date(\'22-2-1981\',\'dd-mm-yyyy\'),  
 1250, 500, 30  
);
insert into emp  
values(  
 7654, \'MARTIN\', \'SALESMAN\', 7698,  
 to_date(\'28-9-1981\',\'dd-mm-yyyy\'),  
 1250, 1400, 30  
);
insert into emp  
values(  
 7844, \'TURNER\', \'SALESMAN\', 7698,  
 to_date(\'8-9-1981\',\'dd-mm-yyyy\'),  
 1500, 0, 30  
);

insert into emp  
values(  
 7876, \'ADAMS\', \'CLERK\', 7788,  
 to_date(\'13-JUL-87\', \'dd-mm-rr\') - 51,  
 1100, null, 20  
);
insert into emp  
values(  
 7900, \'JAMES\', \'CLERK\', 7698,  
 to_date(\'3-12-1981\',\'dd-mm-yyyy\'),  
 950, null, 30  
);
insert into emp  
values(  
 7934, \'MILLER\', \'CLERK\', 7782,  
 to_date(\'23-1-1982\',\'dd-mm-yyyy\'),  
 1300, null, 10  
);
View Code

 

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

--显示各部门员工的工资,并附带显示该部分的最高工资。
SELECT E.DEPTNO,
       E.EMPNO,
       E.ENAME,
       E.SAL,
       LAST_VALUE(E.SAL) 
       OVER(PARTITION BY E.DEPTNO 
            ORDER BY E.SAL ROWS 
            --unbounded preceding and unbouned following针对当前所有记录的前一条、后一条记录,也就是表中的所有记录
            --unbounded:不受控制的,无限的
            --preceding:在...之前
            --following:在...之后
            BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) MAX_SAL
  FROM EMP E;

运行结果:

 

 4.2 示例目的:按照deptno分组,然后计算每组值的总和

SELECT EMPNO,
       ENAME,
       DEPTNO,
       SAL,
       SUM(SAL) OVER(PARTITION BY DEPTNO ORDER BY ENAME) max_sal
  FROM SCOTT.EMP;

运行结果:

 

 4.3 对各部门进行分组,并附带显示第一行至当前行的汇总

  -- 对各部门进行分组,并附带显示第一行至当前行的汇总
  SELECT EMPNO,
       ENAME,
       DEPTNO,
       SAL,
       --注意ROWS BETWEEN unbounded preceding AND current row  是指第一行至当前行的汇总
       SUM(SAL) OVER(PARTITION BY DEPTNO 
                     ORDER BY ENAME 
                     ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) max_sal
  FROM SCOTT.EMP;
  

运行结果:

 

4.4 当前行至最后一行的汇总

-- 当前行至最后一行的汇总
  SELECT EMPNO,
       ENAME,
       DEPTNO,
       SAL,
       SUM(SAL) OVER(PARTITION BY DEPTNO 
                     ORDER BY ENAME 
                     ROWS BETWEEN CURRENT ROW  AND UNBOUNDED FOLLOWING) max_sal
  FROM SCOTT.EMP;

运行结果

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

 -- 当前行的上一行(rownum-1)到当前行的汇总
    SELECT EMPNO,
       ENAME,
       DEPTNO,
       SAL,
       --注意ROWS BETWEEN 1 preceding AND current row 是指当前行的上一行(rownum-1)到当前行的汇总 
       SUM(SAL) OVER(PARTITION BY DEPTNO 
                     ORDER BY ENAME 
                     ROWS BETWEEN 1 preceding  AND current row) max_sal
  FROM SCOTT.EMP;

运行结果

4.6  当前行的上一行(rownum-1)到当前行的下1行(rownum+1)的汇总     

-- 当前行的上一行(rownum-1)到当前行的下辆行(rownum+2)的汇总     
  SELECT EMPNO,
       ENAME,
       DEPTNO,
       SAL,
       --注意ROWS BETWEEN 1 preceding AND 1 following 是指当前行的上一行(rownum-1)到当前行的下1行(rownum+1)的汇总
       SUM(SAL) OVER(PARTITION BY DEPTNO 
                     ORDER BY ENAME 
                     ROWS BETWEEN 1 preceding  AND 1 following) max_sal
  FROM SCOTT.EMP;

结果

 

参考:https://www.cnblogs.com/xlht/p/6261856.html

 

以上是关于利用Oracle分析函数实现多行数据合并为一行的主要内容,如果未能解决你的问题,请参考以下文章

求助,oracle多行数据合并成一行

mysql 多行(GROUP_CONCAT)和多列(CONCAT)的合并函数

oracle中如何把表中具有相同值列的多行数据合并成一行

怎样将多行数字合并到一行中?

[Oracle]行列转换(行合并与拆分)

PostgreSQL合并多行数据为一行,string_agg函数