如何用Pivot实现行列转换

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

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

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

基本语法如下:

SELECT ....
FROM <table-expr>
   PIVOT
     (
      aggregate-function(<column>)
      FOR <pivot-column> IN (<value1>, <value2>,..., <valuen>)
        ) AS <alias>
WHERE .....

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

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

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

CREATE VIEW emp_view AS
SELECT
 deptno,job,to_char(hiredate,‘yyyy‘) hiredate,
 count(*) cnt,sum(sal) sum_sal
FROM emp
GROUP BY deptno,job,to_char(hiredate,‘yyyy‘);

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

2> 视图的数据如下:

SQL> select * from emp_view;

    DEPTNO JOB       HIRE        CNT    SUM_SAL
---------- --------- ---- ---------- ----------
        20 CLERK     1980          1        800
        20 ANALYST   1981          1       3000
        20 ANALYST   1987          1       3000
        30 CLERK     1981          1        950
        30 MANAGER   1981          1       2850
        10 MANAGER   1981          1       2450
        30 SALESMAN  1981          4       5600
        20 MANAGER   1981          1       2975
        10 PRESIDENT 1981          1       5000
        10 CLERK     1982          1       1300
        20 CLERK     1987          1       1100

11 rows selected.

应用场景一:

基本的Pivot转换

例1:

SELECT * FROM
( SELECT deptno,hiredate,cnt
  FROM emp_view
 ) PIVOT (SUM(cnt)
   FOR hiredate IN (‘1980‘ AS "1980",‘1981‘ AS "1981",
                    ‘1982‘ AS "1982",‘1987‘ AS "1987"))
ORDER BY deptno;

    DEPTNO       1980       1981       1982       1987
---------- ---------- ---------- ---------- ----------
        10                     2          1
        20          1          2                     2
        30                     6

3 rows selected.

例2:

SELECT * FROM
( SELECT deptno,job,cnt
  FROM emp_view
 ) PIVOT (SUM(cnt)
   FOR job IN (‘CLERK‘,‘ANALYST‘,‘MANAGER‘,‘SALESMAN‘,‘PRESIDENT‘))
ORDER BY deptno;

    DEPTNO    ‘CLERK‘  ‘ANALYST‘  ‘MANAGER‘ ‘SALESMAN‘ ‘PRESIDENT‘
---------- ---------- ---------- ---------- ---------- -----------
        10          1                     1                      1
        20          2          2          1
        30          1                     1          4

3 rows selected.

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

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

应用场景二:

对多列进行Pivot转换

SELECT * FROM
( SELECT deptno,job,hiredate,cnt
  FROM emp_view
 ) PIVOT (SUM(cnt)
            FOR (job,hiredate) IN
              ((‘CLERK‘,‘1980‘) AS clerk_1980,
               (‘CLERK‘,‘1981‘) AS clerk_1981,
               (‘ANALYST‘,‘1987‘) AS analyst_1987,
               (‘MANAGER‘,‘1981‘) AS manager_1981
              )
           )
ORDER by deptno;

    DEPTNO CLERK_1980 CLERK_1981 ANALYST_1987 MANAGER_1981
---------- ---------- ---------- ------------ ------------
        10                                               1
        20          1                       1            1
        30                     1                         1

3 rows selected.

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

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

应用场景三:

用Pivot实现多个聚合

SELECT * FROM
( SELECT deptno,hiredate,cnt,sum_sal
  FROM emp_view
 ) PIVOT ( SUM(cnt) AS cnt,
           SUM(sum_sal) AS sum_sal
           FOR hiredate IN (‘1980‘,‘1981‘,‘1982‘,‘1987‘))
ORDER BY deptno;

    DEPTNO ‘1980‘_CNT ‘1980‘_SUM_SAL ‘1981‘_CNT ‘1981‘_SUM_SAL ‘1982‘_CNT ‘1982‘_SUM_SAL ‘1987‘_CNT ‘1987‘_SUM_SAL
---------- ---------- -------------- ---------- -------------- ---------- -------------- ---------- --------------
        10                                    2           7450          1           1300
        20          1            800          2           5975                                    2           4100
        30                                    6           9400

3 rows selected.

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

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

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

UNPIVOT

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

CREATE TABLE T1 AS
SELECT * FROM
( SELECT deptno,hiredate,cnt,sum_sal
  FROM emp_view
 ) PIVOT ( SUM(cnt) AS cnt,
           SUM(sum_sal) AS sum_sal
           FOR hiredate IN (‘1980‘ AS "1980",‘1981‘ AS "1981",
                            ‘1982‘ AS "1982",‘1987‘ AS "1987"))
ORDER BY deptno

表T1的结果为:

SQL> select * from t1;

    DEPTNO   1980_CNT 1980_SUM_SAL   1981_CNT 1981_SUM_SAL   1982_CNT 1982_SUM_SAL   1987_CNT 1987_SUM_SAL
---------- ---------- ------------ ---------- ------------ ---------- ------------ ---------- ------------
        10                                  2         7450          1         1300
        20          1          800          2         5975                                  2         4100
        30                                  6         9400

3 rows selected.

首先进行一维unpivot

SELECT deptno,DECODE(hiredate,‘1980_CNT‘,‘1980‘,‘1981_CNT‘,‘1981‘,‘1982_CNT‘,‘1982‘,‘1987_CNT‘,‘1987‘) AS hiredate,cnt
FROM T1
UNPIVOT INCLUDE NULLS
( cnt
  FOR hiredate IN ("1980_CNT","1981_CNT","1982_CNT","1987_CNT"));

    DEPTNO HIRE        CNT
---------- ---- ----------
        10 1980
        10 1981          2
        10 1982          1
        10 1987
        20 1980          1
        20 1981          2
        20 1982
        20 1987          2
        30 1980
        30 1981          6
        30 1982
        30 1987

12 rows selected.

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

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

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

    DEPTNO HIRE        CNT
---------- ---- ----------
        10 1981          2
        10 1982          1
        20 1980          1
        20 1981          2
        20 1987          2
        30 1981          6

6 rows selected.

下面,我们再进行二维unpivot

SELECT deptno,hiredate,cnt,sum_sal
FROM T1
UNPIVOT
( (cnt,sum_sal)
  FOR hiredate IN (("1980_CNT","1980_SUM_SAL") AS 1980,
                   ("1981_CNT","1981_SUM_SAL") AS 1981,
                   ("1982_CNT","1982_SUM_SAL") AS 1982,
                   ("1987_CNT","1987_SUM_SAL") AS 1987));

    DEPTNO   HIREDATE        CNT    SUM_SAL
---------- ---------- ---------- ----------
        10       1981          2       7450
        10       1982          1       1300
        20       1980          1        800
        20       1981          2       5975
        20       1987          2       4100
        30       1981          6       9400

6 rows selected.

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

参考文档:

SQL for Analysis and Reporting

时间: 2024-10-12 14:28:22

如何用Pivot实现行列转换的相关文章

在报表中录入数据时如何实现行列转换

应用场景及需求: 某电力集团在实际应用中需要在填报数据时将数据库中的字段以更符合业务人员使用习惯的方式进行呈现,其中就有行列转换的情况.在常规的应用开发中,一般都是通过专门设计的数据界面进行录入,然后再通过程序转换存入数据库.而现在有了集算器支持的润乾填报表,就能够处理各种有关数据结构的填报需求了,这种行列转换自然也不在话下. 首先,我们来看一下行列转换的准确含义: 在一些企业应用中,一些指标项在数据库存储时会做为不同的字段存储,这样数据结构会更加清晰,但是在报表展示或填报表数据录入时,又需要将

SQL中PIVOT 行列转换

SQL中PIVOT 行列转换 本文导读:T-SQL语句中,Pivot运算符用于在列和行之间对数据进行旋转或透视转换,PIVOT命令可以实现数据表的列转行,同时执行聚合运算,UNPIVOT则与其相反,实现数据的行转列. PIVOT通过将表达式某一列中的唯一值转换为输出中的多个列来旋转表值表达式,并在必要时对最终输出中所需的任何其余列值执行聚合.UNPIVOT与PIVOT执行相反的操作,将表值表达式的列转换为列值. 通俗简单的说:PIVOT就是行转列,UNPIVOT就是列传行 一.PIVOT实例 1

行列转换小结 Pivot ,Unpivot (转,改)

行专列 Pivot 1)SQL 2000版本 静态 SELECT ID , SUM(CASE Code WHEN 'Item1' THEN Value ELSE 0 END) AS Item1 , SUM(CASE Code WHEN 'Item2' THEN Value ELSE 0 END) AS Item2 , SUM(CASE Code WHEN 'Item3' THEN Value ELSE 0 END) AS Item3 FROM RowToCol GROUP BY ID --或者用

SQL Server 使用 Pivot 和 UnPivot 实现行列转换

对于行列转换的数据,通常也就是在做报表的时候用的比较多,之前也零零散散的看了一些,今天就来总结一下. 先创建一个用于演示的临时表: create table #temp ( 年份 nvarchar(10) null, 月份 nvarchar(10) null, 数量 int null ) insert into #temp(年份,月份,数量) select '2015','1','5645' union select '2015','2','1234' union select '2015','

SQL Server中行列转换 Pivot UnPivot

SQL Server中行列转换 Pivot UnPivot PIVOT用于将列值旋转为列名(即行转列),在SQL Server 2000可以用聚合函数配合CASE语句实现 PIVOT的一般语法是:PIVOT(聚合函数(列) FOR 列 in (-) )AS P 完整语法: table_source PIVOT( 聚合函数(value_column) FOR pivot_column IN(<column_list>) ) UNPIVOT用于将列明转为列值(即列转行),在SQL Server 2

SQL Fundamentals: 子查询 || 行列转换(PIVOT,UNPIVOT,DECODE),设置数据层次(LEVEL...CONNECT BY)

SQL Fundamentals || Oracle SQL语言 子查询(基础) 1.认识子查询 2.WHERE子句中使用子查询 3.在HAVING子句中使用子查询 4.在FROM子句中使用子查询 5.在SELECT子句中使用子查询 6.WITH子句 子查询(进阶) 7.分析函数 8.行列转换 9.设置数据层次 八.行列转换 pivot和unpivot函数是Oracle 11g增加的新函数,利用此函数可以实现行列转换操作 按照原始方式实现,使用通用函数中的DECODE()函数 列字段的处理 SQ

SQL Server SQL性能优化之--pivot行列转换减少扫描计数优化查询语句

先看常用的一种表结构设计方式: 那么可能会遇到一种典型的查询方式,主子表关联,查询子表中的某些(或者全部)Key点对应的Value,横向显示(也即以行的方式显示) 这种查询方式很明显的一个却显示多次对字表查询(暂时抛开索引) 相比这种查询方式很多人都遇到过,如果子表是配置信息之类的小表的话,问题不大,如果字表数据量较大,可能就会有影响了. 这个查询目的是将”纵表”存储的结果“横向”显示,相当于横列转换的感觉了.可以将子表的结果一次性将纵表的结果转换成横标,再跟主表连接,然后得到一个最终一样的查询

T-SQL PIVOT 行列转换

前面几个例子: http://www.cnblogs.com/insus/articles/1969896.html http://www.cnblogs.com/insus/articles/1970577.html http://www.cnblogs.com/insus/articles/1970707.html 全是SQL Server行列转换或是列行转换的相关例子.不过在SQL Server2005版本开始,有一个新的函数PIVOT()可以很轻易实现这些功能.参考下面代码: SELEC

SQL行列转换6种方法

在进行报表开发时,很多时候会遇到行列转换操作,很对开发人员针对于SQL级别行列转换操作一直不甚理解,今天正好抽空对其进行了一些简单的总结.这里主要列举3种可以实现SQL行列转换的方法,包括通用SQL解法以及Oracle支持解法. 一.测试数据 测试表依旧采用Oracle经典的scott模式下的dept表和emp表,结构如下: DEPT: create table DEPT ( DEPTNO NUMBER(2) not null, DNAME VARCHAR2(14), LOC VARCHAR2(