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以逗号分隔的字符串拆分为多行数据实例详解

    Oracle以逗号分隔的字符串拆分为多行数据实例详解

    做多选功能时为了简便,会在某个字段中存储多个值,保存时虽然省事,但后续的查询统计时还需要拆分数据才行,因此这时需要将字段内的值分成多行以便后续使用,这篇文章主要给大家介绍了关于Oracle以逗号分隔的字符串拆分为多行数据的相关资料,需要的朋友可以参考下
    2021-07-07
  • Oracle往某表批量插入记录的几种实现方法

    Oracle往某表批量插入记录的几种实现方法

    这篇文章主要给大家介绍了关于Oracle往某表批量插入记录的几种实现方法,Oracle批量插入语句与其他数据库不同,文中通过代码实例介绍的非常详细,需要的朋友可以参考下
    2023-07-07
  • oracle查询一天前、几天前、几小时前、一小时前的数据以及恢复误删的数据

    oracle查询一天前、几天前、几小时前、一小时前的数据以及恢复误删的数据

    这几天工作经常需要查询某天之前的数据,整理了下相关的查询,下面这篇文章主要给大家介绍了关于oracle查询一天前、几天前、几小时前、一小时前的数据以及恢复误删的数据,需要的朋友可以参考下
    2024-03-03
  • 使用oracle修改表字段长度的步骤

    使用oracle修改表字段长度的步骤

    这篇文章主要介绍了如何使用oracle修改表字段长度,本文仅仅简单介绍了如何使用oracle修改表字段长度,而oracle提供了大量能使我们快速便捷地处理数据的函数和方法,需要的朋友可以参考下
    2023-07-07
  • oracle远程连接服务器数据库图文教程

    oracle远程连接服务器数据库图文教程

    这篇文章主要为大家详细介绍了oracle远程连接服务器数据库的图文教程,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-09-09
  • Oracle sqlldr导入一个日期列实例详解

    Oracle sqlldr导入一个日期列实例详解

    这篇文章主要介绍了Oracle sqlldr导入一个日期列实例详解的相关资料,需要的朋友可以参考下
    2017-06-06
  • Oracle dbf文件移动的方法

    Oracle dbf文件移动的方法

    这篇文章主要介绍了Oracle dbf文件移动的方法,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-05-05
  • oracle中 procedure(存储过程)和function(函数)本质区别

    oracle中 procedure(存储过程)和function(函数)本质区别

    这篇文章主要介绍了 oracle中 procedure(存储过程)和function(函数)本质区别,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2017-02-02
  • ORACLE应用经验(2)

    ORACLE应用经验(2)

    ORACLE应用经验(2)...
    2007-03-03
  • Oracle数据库密码复杂度校验脚本utlpwdmg.sql详细解析

    Oracle数据库密码复杂度校验脚本utlpwdmg.sql详细解析

    我们都知道密码策略加固的参数一般包括密码长度、复杂度检测、最大最小使用时间、过期警报时间、最大登录失败次数以及锁定时间等设置,下面这篇文章主要给大家介绍了关于Oracle数据库密码复杂度校验脚本utlpwdmg.sql详细解析的相关资料,需要的朋友可以参考下
    2024-04-04

最新评论