Oracle中行列转换的实现方法汇总

 更新时间:2024年02月05日 09:33:17   作者:树贤森  
行列转换是指将行数据转换为列数据,或将列数据转换为行数据的过程,本文主要介绍了Oracle中行列转换的实现方法汇总,用PIVOT和UNPIVOT函数来实现,具有一定的参考价值,感兴趣的可以了解一下

行列转换是指将行数据转换为列数据,或将列数据转换为行数据的过程。这通常使用的办法是用PIVOT和UNPIVOT函数来实现。这里描述两种方法分别实现行列转换!!!

首先创建表:

  • 学生表:student;--包括学生号,姓名,年纪,性别,生日
  • 教师表:teacher;--包括教师编号,姓名
  • 课程表:course;--包括课程编号,课程名称,对应教师
  • 学生成绩表:sc;--包括学生号,课程编号,成绩

创建表的脚本如下:

--学生 student表
drop table student;
create table student(
sno varchar2(10) primary key,
sname varchar2(20),
sage number(2),
ssex varchar2(5),
birthday date
);
--教师 teacher表
drop table teacher;
create table teacher(
tno varchar2(10) primary key,
tname varchar2(20)
);
--课程 course表
drop table course;
create table course(
cno varchar2(10),
cname varchar2(20),
tno varchar2(20),
constraint pk_course primary key (cno,tno)
);
--学生成绩 sc表
drop table sc;
create table sc(
sno varchar2(10),
cno varchar2(10),
score number(4,2),
constraint pk_sc primary key (sno,cno)
);
/*******初始化学生表的数据******/
insert into student values ('s001','张亍卬',FLOOR(months_between(SYSDATE,date '2000-3-5')/12),'男',date '2000-3-5');
insert into student values ('s002','李殳戋',FLOOR(months_between(SYSDATE,date '2001-2-3')/12),'男',date '2001-2-3');
insert into student values ('s003','吴仝玓',FLOOR(months_between(SYSDATE,date '2002-5-8')/12),'男',date '2002-5-8');
insert into student values ('s004','琴甪',FLOOR(months_between(SYSDATE,date '2000-6-15')/12),'女',date '2000-6-15');
insert into student values ('s005','王讱纩',FLOOR(months_between(SYSDATE,date '2000-8-12')/12),'女',date '2000-8-12');
insert into student values ('s006','李孖伣',FLOOR(months_between(SYSDATE,date '2001-9-20')/12),'男',date '2001-9-20');
insert into student values ('s007','刘辿吒',FLOOR(months_between(SYSDATE,date '2002-10-5')/12),'男',date '2002-10-5');
insert into student values ('s008','萧竦俐',FLOOR(months_between(SYSDATE,date '2003-6-1')/12),'女',date '2003-6-1');
insert into student values ('s009','陈闫邠邡',FLOOR(months_between(SYSDATE,date '2001-1-15')/12),'女',date '2001-1-15');
insert into student values ('s010','陈芃伋',FLOOR(months_between(SYSDATE,date '2001-1-9')/12),'女',date '2001-1-9');
commit;
/******************初始化教师表***********************/
insert into teacher values ('t001', '龚阳');
insert into teacher values ('t002', '谌燕');
insert into teacher values ('t003', '武明星');
commit;
/***************初始化课程表****************************/
insert into course values ('c001','J2SE','t002');
insert into course values ('c002','Java Web','t001');
insert into course values ('c003','SSH','t001');
insert into course values ('c004','Oracle','t001');
insert into course values ('c005','SQL SERVER 2005','t003');
insert into course values ('c006','C#','t003');
insert into course values ('c007','JavaScript','t003');
insert into course values ('c008','DIV+CSS','t001');
insert into course values ('c009','PHP','t003');
insert into course values ('c010','EJB3.0','t002');
commit;
/***************初始化成绩表***********************/
insert into sc values ('s001','c001',78);
insert into sc values ('s002','c001',80);
insert into sc values ('s003','c001',81);
insert into sc values ('s004','c001',60);
insert into sc values ('s001','c002',82);
insert into sc values ('s002','c002',72);
insert into sc values ('s003','c002',81);
insert into sc values ('s001','c007',88);
insert into sc values ('s001','c010',73);
insert into sc values ('s002','c003',69);
insert into sc values ('s002','c008',92);
insert into sc values ('s002','c009',81);
insert into sc values ('s002','c007',85);
insert into sc values ('s002','c010',75);
insert into sc values ('s005','c001',63);
insert into sc values ('s005','c002',96);
insert into sc values ('s005','c007',75);
insert into sc values ('s005','c010',72);
insert into sc values ('s006','c001',72);
insert into sc values ('s007','c001',61);
insert into sc values ('s008','c001',92);
insert into sc values ('s009','c001',58);
insert into sc values ('s010','c001',85);
insert into sc values ('s002','c004',80);
insert into sc values ('s002','c005',70);
insert into sc values ('s002','c006',60);
commit;

一、行转列(一)

使用case when/decode+聚合函数+group by的方法实现行转列;

把sc表进行行转列查询出每个学生每门课程的成绩:

原sc表:

SELECT * FROM sc;--学生成绩表

执行结果展示其中一部分:

 此时要对cno课程编号进行行转列:

select sno,sum(case cno when 'c001' then score end) c001,
           sum(case cno when 'c002' then score end) c002,
           sum(case cno when 'c003' then score end) c003,
           sum(case cno when 'c004' then score end) c004,
           sum(case cno when 'c005' then score end) c005,
           sum(case cno when 'c006' then score end) c006,
           sum(case cno when 'c007' then score end) c007,
           sum(case cno when 'c008' then score end) c008,
           sum(case cno when 'c009' then score end) c009,
           sum(case cno when 'c0010' then score end) c0010
from sc
group by sno  
order by sno;

执行结果:

展示的为每个学生他的每一门课程成绩;

总结:

  • 要求把查询的哪一列转成列名就放在case后面,并把它的列中值进行分类放在when后面;
  • 比如学生成绩表总共就三列(学生号,课程编号,学生成绩),我们要查询每个学生的每科成绩展示,就需要对课程编号cno进行分类转换,因此把课程编号cno放在case后面,然后把课程编号cno中所包含的所有值进行分类,即全部课程科目c001--c0010,分类放在when的后面!!
  • 要把哪一列内容放在列中值中就放在then 后面;
  • 意思就是我们最后要看的结果值,比如对应上面查询,要查看的是学生成绩score,此时就把学生成绩score放在then的后面即可。
  • 这种办法可以实现我们对需求的解决实现,但是使用比较麻烦,可能会理解错误,而且代码语句写的比较多,因此可以换种方法来更简单实现行转列!!!

二、行转列(二) 

使用PIVOT函数,可以将行数据转换为列数据,并且可以在同一查询中汇总和筛选数据。

基本语法格式如下:

PIVOT(被聚合的列 FOR 行转列的列 in(列中值1,列中值2...))

select *
from  表
pivot (聚合函数(被聚合的列) for 行转列的列 in (列中值1,,列中值2植..))

批注:

被聚合的列:变成列中值的列;

行转列的列:由列中值变为列名的列 ; 

列中值1,列中值2..:新增加的列名(即为行转列的列中的列中值)就是列中值1,列中值2...。

备注:被聚合的列要加聚合函数。

或者另一种理解:

SELECT *
FROM (SELECT column1, column2, column3 FROM table_name)
PIVOT (aggregate_function(column2) 
       FOR column1 IN ('value1' AS alias1, 'value2' AS alias2, ...));

其中,PIVOT中的column1是要转换为列的列,column2是要汇总的列,alias是列的别名。

那么此时“把sc表行转列查询每个学生每门课程的成绩”可写为:

select *
from sc
pivot(sum(score) for cno in('c001' c001,'c002' c002,'c003' c003,'c004' c004,
                'c005' c005,'c006' c006,'c007' c007,'c008' c008,'c009' c009,'c010' c010))
order by sno;

其中,as可加可不加,对于列中值一定要加单引号。同时运行结果是和之前的一致。如图所示:

使用PIVOT函数时,需要注意以下几点:

  • PIVOT函数必须在FROM子句中使用,因此需要将原始查询包装在一个子查询中。
  • aggregate_function是要应用于column2的聚合函数,可以是SUM、AVG、COUNT、MAX、MIN等。
  • FOR子句指定要在新列中显示的值。在IN子句中指定这些值,并在别名中指定新列的名称。

三、列转行(一)

使用union all方法实现列转行;

比如:有一张员工表emp,请用一条sql显示如下格式

  ENPNO  KEY     VALUE 
  7369  ENAME    SMITH
  7369  JOB      CLERK
  7369  MGR      7902

先看原员工表格式:

select * from emp;

 通过对比发现是将原表中的列和其对应值转换为行式展现,同时为其定义了新的列名分别为ENPNO 、KEY 、VALUE 。那么用union all的方式实现的语句为:

select * from 
  (select empno,'ENAME' KEY,ENAME VALUE FROM EMP 
  UNION ALL
  select empno,'JOB' KEY,TO_CHAR(JOB) VALUE FROM EMP
  UNION ALL
  select empno,'MGR' KEY,TO_CHAR(MGR) VALUE FROM EMP
  UNION ALL
  select empno,'HIREDATE' KEY,TO_CHAR(HIREDATE) VALUE FROM EMP
  UNION ALL
  select empno,'SAL' KEY,TO_CHAR(SAL) VALUE FROM EMP
  UNION ALL
  select empno,'COMM' KEY,TO_CHAR(COMM) VALUE FROM EMP
  UNION ALL
  select empno,'DEPTNO' KEY,TO_CHAR(DEPTNO) VALUE FROM EMP)
WHERE EMPNO=7369;

简单理解为:查询该员工编号对应的每一条列信息,对列中值进行格式转换统一,然后使用union all进行并集为一个数据集合作为参考表,最后加判断条件完成列转换。那么看下这种方式的运行结果:

通过改图发现确实已经完成了目的需求的格式转换。不过此方法同样比较繁琐,代码量也比较多,所以可以换另外一种方法实现同样的效果。

四、列转行(二)

UNPIVOT函数可以将列数据转换为行数据。基本语法如下:

unpivot 列转行自动去空 如果要留住空值 在unpivot 后加上 include nulls

unpivot(被聚合的列的新列名 for  列转行的列的新列名 in (字段1,字段2...))

被聚合的列的新列名:指的是目标结果集的列名,按照目标结果集来填写,即原来聚合的数据如这里的nums,列转行之前的列中值放在取了新名字的这个列中;
列转行的列的新列名:指的是要列转行的列名的集合新名字,既创建一个新的列来存储要列转行的列,如这里的name,他的列中值在列传行之前为原视图的多个列;
字段1,字段2...:指的是要列转行的列名,既为要放到列转行的列的新列名里的列中值,就是列转行之前视图的多个列。

完整格式:

SELECT *
FROM table_name
UNPIVOT (column3 FOR column1 IN (column2, column3, ...));

column1是要转换为行的列,column2和column3是要转换的列。

那么此时使用UNPIVOT函数完成上个问题的列转行方法就可以写为:

select *
from (SELECT empno,ENAME,JOB,
             TO_CHAR(MGR) MGR,
             TO_CHAR(HIREDATE) HIREDATE,
             TO_CHAR(SAL) SAL,
             TO_CHAR(COMM) COMM,
             TO_CHAR(DEPTNO) DEPTNO
      FROM EMP
      WHERE EMPNO=7369)
unpivot include nulls(VALUE for KEY IN(ENAME,JOB,MGR,HIREDATE,SAL,COMM,DEPTNO));

这里在列转行时对表中每一列做了格式统一,最后运行结果和第一种方法一样。如图所示:

使用UNPIVOT函数时,需要注意以下几点:

  • UNPIVOT函数必须在FROM子句中使用,因此需要将原始查询包装在一个子查询中。
  • FOR子句指定要转换为行的列。
  • IN子句指定要转换的列。

到此这篇关于Oracle中行列转换的实现方法汇总的文章就介绍到这了,更多相关Oracle 行列转换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Oracle 11g 客户端的安装和配置的图文教程

    Oracle 11g 客户端的安装和配置的图文教程

    这篇文章主要介绍了 Oracle 11g 客户端的安装和配置的图文教程,需要的朋友可以参考下
    2017-05-05
  • Oracle如何更改表空间的数据文件位置详解

    Oracle如何更改表空间的数据文件位置详解

    这篇文章主要给大家介绍了关于Oracle如何更改表空间的数据文件位置,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧。
    2017-11-11
  • oracle 10g 快照操作方法

    oracle 10g 快照操作方法

    本文将详细介绍oracle 10g 快照操作方法包括创建、刷新、修改等,需要了解的朋友可以参考下
    2012-12-12
  • Oracle实现细粒度访问控制的步骤

    Oracle实现细粒度访问控制的步骤

    细粒度访问控制是Oracle数据库中用于提供行级和列级安全控制的强大功能,本文主要给大家介绍了Oracle实现细粒度访问控制的步骤,并通过代码示例讲解的非常详细,需要的朋友可以参考下
    2024-09-09
  • 在Oracle表中进行关键词搜索的过程

    在Oracle表中进行关键词搜索的过程

    这篇文章主要介绍了在Oracle表中进行关键词搜索的过程,我们可以通过Oracle SQL Developer或Toad运行PL/SQL块,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-06-06
  • oracle11g卸载完整图文教程

    oracle11g卸载完整图文教程

    这篇文章主要为大家详细介绍了oracle11g卸载完整图文教程,文中安装步骤介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09
  • Oracle行级锁的特殊用法简析

    Oracle行级锁的特殊用法简析

    Oracle有许多的锁,各种锁的效用是不一样的。下面重点介绍Oracle行级锁,Oracle行级锁只对用户正在访问的行进行锁定。可以更好的保证数据的安全性,需要的朋友可以了解下
    2012-11-11
  • Navicat连接Oracle数据库的详细步骤与注意事项

    Navicat连接Oracle数据库的详细步骤与注意事项

    Navicat是一套可创建多个连接的数据库管理工具,用以方便管理各种数据库,下面这篇文章主要给大家介绍了关于Navicat连接Oracle数据库的详细步骤与注意事项,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2023-04-04
  • Oracle的RBO和CBO详细介绍和优化模式设置方法

    Oracle的RBO和CBO详细介绍和优化模式设置方法

    这篇文章主要介绍了Oracle的RBO和CBO详细介绍和优化模式设置方法,RBO即基于规则的优化方式(Rule-Based Optimization),CBO即基于代价的优化方式(Cost-Based Optimization),需要的朋友可以参考下
    2014-07-07
  • 在Oracle中导入dmp文件的方法

    在Oracle中导入dmp文件的方法

    这篇文章主要介绍了如何在Oracle中导入dmp文件,很简单,但很实用,需要的朋友可以参考下
    2014-09-09

最新评论