如何用Pivot实现行列转换

数据库 Oracle
在Oracle中,如果要实现行列转换,较为常见的是用DECODE和CASE语句。对于简单的行列转行,DECODE和CASE语句尚能应付。在逻辑比较复杂,分组聚合较多的场景中,DECODE和CASE语句则力有不逮。而pivot则可完美解决这一切。

   首先,我们来看看Oracle对于其的解释:

 

  可见,pivot是数据仓库中的关键技术,它利用交叉查询(crosstabulation query)将行转换为列。

  基本语法如下:

 

  1. SELECT .... 
  2. FROM <table-expr> 
  3.    PIVOT 
  4.      ( 
  5.       aggregate-function(<column>) 
  6.       FOR <pivot-columnIN (<value1>, <value2>,..., <valuen>) 
  7.         ) AS <alias> 
  8. WHERE ..... 

 

  下面我们来通过具体的案例对其进行阐述。

  首先,构造案例所需的数据,

  1> 创建视图,以EMP表的数据作为源数据。

 

  1. CREATE VIEW emp_view AS 
  2. SELECT 
  3.  deptno,job,to_char(hiredate,'yyyy') hiredate, 
  4.  count(*) cnt,sum(sal) sum_sal 
  5. FROM emp 
  6. GROUP BY deptno,job,to_char(hiredate,'yyyy'); 

 

  其中,deptno为部门号,job为工作的类型(即工种),hiredate为雇佣的日期,cnt为特定部门,特定工种在特定年份雇佣的员工的总数,sum_sal为特定部门,特定工种,特定年份雇佣的员工的工资的总和。

  2> 视图的数据如下:

 

  1. SQL> select * from emp_view; 
  2.  
  3.     DEPTNO JOB       HIRE        CNT    SUM_SAL 
  4. ---------- --------- ---- ---------- ---------- 
  5. CLERK     1980          1        800 
  6. ANALYST   1981          1       3000 
  7. ANALYST   1987          1       3000 
  8. CLERK     1981          1        950 
  9. MANAGER   1981          1       2850 
  10. MANAGER   1981          1       2450 
  11. SALESMAN  1981          4       5600 
  12. MANAGER   1981          1       2975 
  13. PRESIDENT 1981          1       5000 
  14. CLERK     1982          1       1300 
  15. CLERK     1987          1       1100 
  16. rows selected. 

 

  应用场景一:

  基本的Pivot转换

  例1:

 

  1. SELECT * FROM 
  2. SELECT deptno,hiredate,cnt 
  3.   FROM emp_view 
  4.  ) PIVOT (SUM(cnt) 
  5.    FOR hiredate IN ('1980' AS "1980",'1981' AS "1981"
  6.                     '1982' AS "1982",'1987' AS "1987")) 
  7. ORDER BY deptno; 
  8.  
  9.     DEPTNO       1980       1981       1982       1987 
  10. ---------- ---------- ---------- ---------- ---------- 
  11.                     2          1 
  12.          1          2                     2 
  13.                     6 
  14. rows selected. 

 

  例2:

 

  1. SELECT * FROM 
  2. SELECT deptno,job,cnt 
  3.   FROM emp_view 
  4.  ) PIVOT (SUM(cnt) 
  5.    FOR job IN ('CLERK','ANALYST','MANAGER','SALESMAN','PRESIDENT')) 
  6. ORDER BY deptno; 
  7.  
  8.     DEPTNO    'CLERK'  'ANALYST'  'MANAGER' 'SALESMAN' 'PRESIDENT' 
  9. ---------- ---------- ---------- ---------- ---------- ----------- 
  10.          1                     1                      1 
  11.          2          2          1 
  12.          1                     1          4 
  13. rows selected. 

 

  两例以不同的列进行统计,前者是hiredate,后者是job。

  除此之外,前者用了别名,后面没有用别名,两者的显示效果也是不一样的。

  应用场景二:

  对多列进行Pivot转换

 

  1. SELECT * FROM 
  2. SELECT deptno,job,hiredate,cnt 
  3.   FROM emp_view 
  4.  ) PIVOT (SUM(cnt) 
  5.             FOR (job,hiredate) IN 
  6.               (('CLERK','1980'AS clerk_1980, 
  7.                ('CLERK','1981'AS clerk_1981, 
  8.                ('ANALYST','1987'AS analyst_1987, 
  9.                ('MANAGER','1981'AS manager_1981 
  10.               ) 
  11.            ) 
  12. ORDER by deptno; 
  13.  
  14.     DEPTNO CLERK_1980 CLERK_1981 ANALYST_1987 MANAGER_1981 
  15. ---------- ---------- ---------- ------------ ------------ 
  16.                                               1 
  17.          1                       1            1 
  18.                     1                         1 
  19. rows selected. 

 

  限于篇幅,FOR (job,hiredate) IN语句中没有列出更多组合,只列出了四组,当然,我们可以根据实际场景需要罗列更多的组合。

  从本例中可以看出,对两个列进行Pivot转换可从三个维度呈现统计结果。

  应用场景三:

  用Pivot实现多个聚合

 

  1. SELECT * FROM 
  2. SELECT deptno,hiredate,cnt,sum_sal 
  3.   FROM emp_view 
  4.  ) PIVOT ( SUM(cnt) AS cnt, 
  5.            SUM(sum_sal) AS sum_sal 
  6.            FOR hiredate IN ('1980','1981','1982','1987')) 
  7. ORDER BY deptno; 
  8.  
  9.     DEPTNO '1980'_CNT '1980'_SUM_SAL '1981'_CNT '1981'_SUM_SAL '1982'_CNT '1982'_SUM_SAL '1987'_CNT '1987'_SUM_SAL 
  10. ---------- ---------- -------------- ---------- -------------- ---------- -------------- ---------- -------------- 
  11.                                    2           7450          1           1300 
  12.          1            800          2           5975                                    2           4100 
  13.                                    6           9400 
  14. rows selected. 

 

  '1981'_CNT指的是1981年雇佣的员工的总数,'1981'_SUM_SAL指的是1981年雇佣员工所开出的工资。

  具体到本例中,即1981年10号部门招聘了2位员工,开出的工资合计为7450元,20号部门招聘了2位员工,开出的工资合计为5975元,30号部门招聘了6名员工,开出的工资合计为9400元,依次类推。

  既然有pivot将行转换为列,同样也有unpivot操作将聚合后的列转换为行。

  UNPIVOT

  以上述应用场景三的结果作为源数据进行操作

 

  1. CREATE TABLE T1 AS 
  2. SELECT * FROM 
  3. SELECT deptno,hiredate,cnt,sum_sal 
  4.   FROM emp_view 
  5.  ) PIVOT ( SUM(cnt) AS cnt, 
  6.            SUM(sum_sal) AS sum_sal 
  7.            FOR hiredate IN ('1980' AS "1980",'1981' AS "1981"
  8.                             '1982' AS "1982",'1987' AS "1987")) 
  9. ORDER BY deptno 

 

  表T1的结果为:

 

  1. SQL> select * from t1; 
  2.  
  3.     DEPTNO   1980_CNT 1980_SUM_SAL   1981_CNT 1981_SUM_SAL   1982_CNT 1982_SUM_SAL   1987_CNT 1987_SUM_SAL 
  4. ---------- ---------- ------------ ---------- ------------ ---------- ------------ ---------- ------------ 
  5.                                  2         7450          1         1300 
  6.          1          800          2         5975                                  2         4100 
  7.                                  6         9400 
  8. rows selected. 

 

  首先进行一维unpivot

 

  1. SELECT deptno,DECODE(hiredate,'1980_CNT','1980','1981_CNT','1981','1982_CNT','1982','1987_CNT','1987'AS hiredate,cnt 
  2. FROM T1 
  3. UNPIVOT INCLUDE NULLS 
  4. ( cnt 
  5.   FOR hiredate IN ("1980_CNT","1981_CNT","1982_CNT","1987_CNT")); 
  6.  
  7.     DEPTNO HIRE        CNT 
  8. ---------- ---- ---------- 
  9. 1980 
  10. 1981          2 
  11. 1982          1 
  12. 1987 
  13. 1980          1 
  14. 1981          2 
  15. 1982 
  16. 1987          2 
  17. 1980 
  18. 1981          6 
  19. 1982 
  20. 1987 
  21. rows selected. 

 

  输出的结果为不同部门在不同年份的雇佣人数,

  注意:上述SQL语句中UNPIVOT后加了INCLUDE NULLS,当然也可以指定为EXCLUDE NULLS,即排除cnt为空的值,如果不指定,则默认为EXCLUDE NULLS。

  UNPIVOT后不指定INCLUDE NULLS的输入结果为:

 

  1. DEPTNO HIRE        CNT 
  2. ---------- ---- ---------- 
  3. 1981          2 
  4. 1982          1 
  5. 1980          1 
  6. 1981          2 
  7. 1987          2 
  8. 1981          6 
  9. rows selected. 

 

  下面,我们再进行二维unpivot

 

  1. SELECT deptno,hiredate,cnt,sum_sal 
  2. FROM T1 
  3. UNPIVOT 
  4. ( (cnt,sum_sal) 
  5.   FOR hiredate IN (("1980_CNT","1980_SUM_SAL"AS 1980, 
  6.                    ("1981_CNT","1981_SUM_SAL"AS 1981, 
  7.                    ("1982_CNT","1982_SUM_SAL"AS 1982, 
  8.                    ("1987_CNT","1987_SUM_SAL"AS 1987)); 
  9.  
  10.     DEPTNO   HIREDATE        CNT    SUM_SAL 
  11. ---------- ---------- ---------- ---------- 
  12.       1981          2       7450 
  13.       1982          1       1300 
  14.       1980          1        800 
  15.       1981          2       5975 
  16.       1987          2       4100 
  17.       1981          6       9400 
  18. rows selected. 

 

  输入结果为T1表列转行的结果。

  参考文档:

  SQL for Analysis and Reporting

责任编辑:honglu 来源: 博客园
相关推荐

2011-07-15 09:04:42

PIVOTUNPIVOT

2024-05-17 08:52:43

SQL实用技巧行列转换

2011-03-15 14:26:23

iptablesNAT

2011-03-15 09:10:47

iptablesNAT

2010-03-04 15:24:14

Python程序

2020-05-09 10:38:31

Python透视表数据

2018-03-15 14:07:17

润乾Excel行列转换

2016-09-26 15:14:28

Javascript前端vue

2010-05-24 10:23:34

实现MySQL

2017-10-11 16:19:36

jquery留言框设计

2017-10-27 22:03:35

javascrip

2018-03-30 10:26:24

行间距行高iOS

2010-05-25 13:47:53

MySQL 命令

2021-03-02 10:57:39

二叉树二叉堆节点

2017-04-26 08:31:10

神经网络自然语言PyTorch

2009-02-05 14:17:37

FTP服务器Java

2023-10-26 11:03:50

C语言宏定义

2018-08-31 09:55:38

Ansible网络自动化

2015-07-09 09:49:39

PHPSocket服务器

2015-07-06 13:36:14

Redis微博关注关系
点赞
收藏

51CTO技术栈公众号