Oracle中行列转换两种实现方法
作者:龙西
前言
行列转换是指将行数据转换为列数据,或将列数据转换为行数据的过程。这通常使用的办法是用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行列转换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!