Oracle数据库中行列转换

news2024/9/9 7:37:55

在Oracle数据库中,行转列通常涉及到将表中的多行数据转换为列的形式,这种操作在数据报表和分析中非常常见。Oracle数据库没有直接的函数或命令可以直接完成这种转换,但可以通过几种不同的方式来实现,包括使用CASE语句、PIVOT操作以及动态SQL。下面是一些实现行转列的方法:

一、行转列

1. 使用CASE语句

我们先查询出每个职位在每个部门中的的总薪资数据

SELECT job,deptno,SUM(sal) AS sum_sal
FROM scott.emp
WHERE deptno IS NOT NULL
GROUP  BY job,deptno
ORDER  BY job, deptno;

JOB       DEPTNO    SUM_SAL
--------- ------ ----------
ANALYST       20       6000
CLERK         10       1300
CLERK         20       1900
CLERK         30        950
MANAGER       10       2450
MANAGER       20       2975
MANAGER       30       2850
PRESIDENT     10       5000
SALESMAN      30       5600

CASE语句可以在SELECT查询中根据条件将行数据转换为列。这种方法在处理固定数量的列时非常有效。

SELECT job,
       SUM(CASE deptno WHEN 10 THEN sal ELSE 0 END) deptno_10_sal,
       SUM(CASE deptno WHEN 20 THEN sal ELSE 0 END) deptno_20_sal,
       SUM(CASE deptno WHEN 30 THEN sal ELSE 0 END) deptno_30_sal
FROM scott.emp
GROUP BY job;
-- 执行结果集
JOB       DEPTNO_10_SAL DEPTNO_20_SAL DEPTNO_30_SAL
--------- ------------- ------------- -------------
CLERK              1300          1900           950
SALESMAN              0             0          5600
PRESIDENT          5000             0             0
MANAGER            2450          2975          2850
ANALYST               0          6000             0

2. 使用PIVOT操作

Oracle 11g及以后版本引入了PIVOT操作符,专门用于将行数据转换为列数据。它使得转换过程更加简洁和直观。

PIVOT 函数的语法是

SELECT ...
FROM   ...
PIVOT
   ( pivot_clause
     pivot_for_clause
     pivot_in_clause )
WHERE  ...

其中:

  • pivot_clause 表示对某个列进行聚合运算
  • pivot_for_clause 表示分组的以及需要行转列的列
  • pivot_in_clause 表示行转列的列名取值范围

PIVOT 语句是在 FROM 子句和 WHERE 子句的中间,我们可以通过 WHERE 子句对行转列后的结果集进行过滤,如下:

WITH cte_data AS (
        SELECT job, deptno, sal
        FROM scott.emp
        WHERE deptno IS NOT NULL
    )
SELECT *
FROM   cte_data
PIVOT (
      SUM(sal)            -- pivot_clause
      FOR deptno          -- pivot_for_clause
      IN  (10,20,30)      -- pivot_in_clause
     );
-- 执行结果集
JOB               10         20         30
--------- ---------- ---------- ----------
CLERK           1300       1900        950
SALESMAN                              5600
PRESIDENT       5000            
MANAGER         2450       2975       2850
ANALYST                    6000 

从上面的输出可以看出,行转列的列名都是数字,我们可以给这些列名指定别名,并指定where 条件如下:

WITH cte_data AS (
        SELECT job, deptno, sal
        FROM scott.emp
        WHERE  deptno IS NOT NULL
    )
SELECT *
FROM   cte_data
PIVOT (
      SUM(sal)                -- 对 sal 进行求和聚合
      FOR deptno              -- 需要对 deptno 进行行转列
      IN  (10 AS DEPTNO_10_sal,
           20 AS DEPTNO_20_sal,
           30 AS DEPTNO_30_sal)     -- 行转列后的列名取值范围
     )
WHERE  job IN ('ANALYST','CLERK','SALESMAN');
-- 执行结果集
JOB       DEPTNO_10_SAL DEPTNO_20_SAL DEPTNO_30_SAL
--------- ------------- ------------- -------------
CLERK              1300          1900           950
SALESMAN                                       5600
ANALYST                          6000 

注意:PIVOT中的IN子句列出了你想要转换为列的所有可能的deptno值。

在聚合的时候我们可以加多个聚合函数,如下

WITH cte_data AS (
        SELECT job, deptno, sal
        FROM scott.emp
        WHERE  deptno IS NOT NULL
    )
SELECT *
FROM   cte_data
PIVOT (
      SUM(sal) AS sum_sal,     		-- 对 sal 进行求和聚合
      COUNT(sal) AS cnt        		-- 对 sal 进行求总数聚合
      FOR deptno               		-- 需要对 deptno 进行行转列
      IN  (10 AS DEPTNO_10_sal,
           20 AS DEPTNO_20_sal,
           30 AS DEPTNO_30_sal)     -- 行转列后的列名取值范围
     )
WHERE  job IN ('ANALYST','CLERK','SALESMAN');

-- 执行结果集
JOB       DEPTNO_10_SAL_SUM_SAL DEPTNO_10_SAL_CNT DEPTNO_20_SAL_SUM_SAL DEPTNO_20_SAL_CNT DEPTNO_30_SAL_SUM_SAL DEPTNO_30_SAL_CNT
--------- --------------------- ----------------- --------------------- ----------------- --------------------- -----------------
CLERK                      1300                 1                  1900                 2                   950                 1
SALESMAN                                        0                                       0                  5600                 4
ANALYST                                         0                  6000                 2                                       0

pivot_for_clause 也可以使用多个字段,如下:

WITH cte_data AS (
        SELECT job, deptno, sal
        FROM   scott.emp
        WHERE  deptno IS NOT NULL
    )
SELECT *
FROM   cte_data
PIVOT (
      SUM(sal) AS sum,          -- 对 sal 进行求和聚合
      COUNT(sal) AS cnt         -- 对 sal 进行求总数聚合
      FOR (deptno, job)         -- 需要对 deptno 和 job 进行行转列
      IN  ((30, 'ANALYST') AS deptno_30_analyst,
           (30, 'CLERK') AS deptno_30_clerk,
           (30, 'SALESMAN') AS deptno_30_salesman)     -- 行转列后的列名取值范围
     );

-- 执行结果集
DEPTNO_30_ANALYST_SUM DEPTNO_30_ANALYST_CNT DEPTNO_30_CLERK_SUM DEPTNO_30_CLERK_CNT DEPTNO_30_SALESMAN_SUM DEPTNO_30_SALESMAN_CNT
--------------------- --------------------- ------------------- ------------------- ---------------------- ----------------------
                                          0                 950                   1                   5600                      4

3. 动态SQL

当列的数量或名称在查询执行时未知时(例如,基于表中数据的动态变化),可以使用动态SQL来构建并执行PIVOT查询或CASE语句。这通常涉及使用PL/SQL来编写一个能够动态构建并执行SQL语句的程序。

结论

选择哪种方法取决于你的具体需求,比如列的数量是否固定、是否需要在运行时动态确定列等。对于简单的固定列转换,CASE语句或PIVOT操作可能就足够了。对于更复杂的动态列转换,你可能需要使用动态SQL。

二、列转行

Oracle数据库中的列转行操作,即将表中的列数据转换成行数据,是一种常见的数据处理需求。以下是一些实现Oracle列转行的方法:

准备测试表及数据

CREATE table scott.t_pivoted_emp 
AS 
SELECT * FROM (
       WITH cte_data AS (
          SELECT job, deptno, sal 
          FROM scott.emp 
          WHERE deptno IS NOT NULL
      )
      SELECT *
      FROM cte_data 
      PIVOT (
            SUM(sal) sum
            FOR deptno
            IN (10 AS deptno_10_sal, 
                20 AS deptno_20_sal,
                30 AS deptno_30_sal)
      )
); 
SQL> select * from scott.t_pivoted_emp;

JOB       DEPTNO_10_SAL_SUM DEPTNO_20_SAL_SUM DEPTNO_30_SAL_SUM
--------- ----------------- ----------------- -----------------
CLERK                  1300              1900               950
SALESMAN                                                   5600
PRESIDENT              5000                   
MANAGER                2450              2975              2850
ANALYST                                  6000 

1. 使用CASE语句和UNION ALL

当UNPIVOT函数不满足需求时,可以使用CASE语句结合UNION ALL来实现更复杂的列转行。这种方法虽然比较繁琐,但灵活性更高。

SELECT * FROM (
  SELECT job, 10 as deptno,DEPTNO_10_SAL_SUM as salary FROM scott.t_pivoted_emp
  UNION 
  SELECT job, 20 as deptno,DEPTNO_20_sal_sum as salary FROM scott.t_pivoted_emp
  UNION 
  SELECT job, 30 as deptno,DEPTNO_30_sal_sum as salary FROM scott.t_pivoted_emp  
)
WHERE salary IS NOT NULL
ORDER BY job, deptno;

JOB           DEPTNO     SALARY
--------- ---------- ----------
ANALYST           20       6000
CLERK             10       1300
CLERK             20       1900
CLERK             30        950
MANAGER           10       2450
MANAGER           20       2975
MANAGER           30       2850
PRESIDENT         10       5000
SALESMAN          30       5600

9 rows selected

这种方法会为每一列生成一个行集合,其中包含了原始列的名称和值。

2. 使用UNPIVOT函数

UNPIVOT是Oracle提供的一个专门用于列转行的函数。它可以将表中的列转换为行,并且指定哪些列需要被转换。基本语法如下:

SELECT job,deptno,salary
FROM scott.t_pivoted_emp
UNPIVOT
     (salary
     FOR deptno 
     IN (DEPTNO_10_SAL_SUM AS 10,DEPTNO_20_SAL_SUM AS 20,DEPTNO_30_SAL_SUM AS 30))
ORDER BY job, deptno;

JOB           DEPTNO     SALARY
--------- ---------- ----------
ANALYST           20       6000
CLERK             10       1300
CLERK             20       1900
CLERK             30        950
MANAGER           10       2450
MANAGER           20       2975
MANAGER           30       2850
PRESIDENT         10       5000
SALESMAN          30       5600

在这个例子中,DEPTNO_10_SAL_SUMDEPTNO_20_SAL_SUM DEPTNO_30_SAL_SUM会被转换成行,而JOB和转换后的值(通过value列表示)会保持为列。

3. 自定义函数和过程

对于非常特定的需求,可能需要编写自定义的PL/SQL函数或过程来实现列转行。这种方法提供了最大的灵活性,但也需要相应的编程技能和对Oracle PL/SQL的深入理解。

总结

在实际应用中,应根据具体需求和数据结构选择最合适的列转行方法。对于大多数常见场景,UNPIVOT函数和CASE语句结合UNION ALL是两种简单且有效的解决方案。对于更复杂的场景,可能需要考虑使用CONNECT BY LEVEL或编写自定义函数。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1963392.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

IDEA某个项目被同事提交的代码导致不能进入Debug了,不是IDEA的问题。千万要避坑!

刚开始我发现突然不能进入debug了,打上去后就立马边灰了,我以为是我IDEA的问题,后来我换了其他项目都能正常进入debug. 而且后续,这个项目的其他同事也都不能进入debug了。 我就怀疑是项目中有人提交了代码导致的。 后来查…

必备插件自取 vue3.0 在vscode提升编码效率

一些vue开发中用到的插件,抽空总结一下,一是方便自己查看,另外是供其他需要的同学参考。在开发vue项目中,无论用什么编辑器一些好用的插件对我们来讲或许是必要的,因为好的插件既能方便我们编码,又能提升开…

文件解析漏洞--IIS--Vulhub

文件解析漏洞 一、IIS解析漏洞 用windowserver2003安装IIS测试 1.1 IIS6.X 方法一:目录解析 在网站下建立文件夹的名字为.asp/.asa的文件夹,其目录内的任何扩展名的文件都被IIS当作asp文件来解析并执行。 1.txt文件里是asp文件的语法查看当前时间 方…

PSO求解函数最小值的MATLAB例程|MATLAB源代码

本篇文章适合PSO入门,进阶的可能会觉得太简单的。 目录 PSO例程作用运行结果代码函数解释 例程修改tips PSO Particle Swarm Optimization,粒子群优化算法,通过模拟鸟群或鱼群的行为来寻找最优解。在计算时通过对一群粒子的位置和速度进行迭…

Stable Diffusion Windows本地部署超详细教程(手动+自动+整合包三种方式)

2022年作为AIGC(Artificial Intelligence Generated Content)时代的元年,各个领域的AIGC技术都有一个迅猛的发展,给工业界、学术界、投资界甚至竞赛界都注入了新的“AI活力”与“AI势能”。 其中在AI绘画领域,Stable D…

内部类练习题

代码: public static void main(String[] args) {A anew A();a.f(); } class A{private String name"A";public void f(){class B{private String name"B";public void show(){System.out.println("A姓名为:"A.this.name…

智慧校园实训管理:打造高效实验项目管理系统

在智慧校园的实训管理框架中,实验项目管理功能是培养学生实践能力和创新精神的关键一环。这一功能通过数字化手段,实现了实验项目的全流程管理,从项目设计、资源调配、过程监控到成果评估,确保了实训活动的高效开展和教学质量的稳…

LabVIEW打开的视频格式IMAQ AVI Open

LabVIEW打开的视频格式IMAQ AVI Open LabVIEW所支持的视频解码器是有限的,不能支持所有的视频解码器。如果解码器选择的不正确,会出现的错误。 错误提示信息如下: Error -1074396009 occurred at IMAQ AVI Open Possible reason(s): An inter…

人工智能大模型发展带来的风险挑战和对策

经过近70年的发展,人工智能技术发展经历了三次起伏,2022年以来,以ChatGPT、Sora等为代表的预训练大模型持续取得突破,推动着人工智能技术从感知向认识,从分析判断式向生成式,从专用向通用进入快速发展的新阶…

提供一个下载国外DockerHub镜像的办法

由于众所周知的一些问题,国内现在下载国外的镜像比较难。尤其是比较新的版本的时候。 比如阿里云加速器的镜像库,skywalking-oap的最新版本也只有8.9,有时候不满足业务需要。官网目前最新10.0.1。很多情况下我们需要9以上版本。 提供几个办…

tomcat使用问题:安装后无法访问localhost:8080

一、tomcat 未启动 所以http://localhost:8080打不开; 二、环境变量未配置 tomcat图标显示已启动,但http://localhost:8080还是打不开,可能是环境变量没有配置好,关于怎么配置环境变量网上到处都是,一下仅供参考: …

halcon_C#联合halcon打开摄像头

1. 创建halcon项目 -> 2.测试连接 -> 3. 在halcon中打开摄像头成功 -> 4. 插入代码 -> 5. 导出为.cs文件 6. 创建VS项目 -> 7.将action部分代码嵌入winform -> 8. 编写代码 -> // 导入HalconDotNet命名空间,这是用于Halcon图像处理的…

MySQL存储引擎和

MySQL存储引擎 在数据库中保存的是一张张有着千丝万缕关系的表,所以表设计的好坏,将直接影响着整个数据库。而在设计表的时候,最关注的一个问题是使用什么存储引擎。MySQL中的数据用各种不同的技术存储在文件(或者内存)中。这些技术中的每一种…

redis面试(四)ZSet数据结构

Sorted Set 有序集合ZSet,但是有序集合的英文明明是sorted sets。 那这个“Z”代表什么意思,这点官网没有解释,但是gitHub上有人问过,作者是这样回答的 Hello. Z is as in XYZ, so the idea is, sets with another dimension: t…

Selenium与WebDriver:Errno 8 Exec格式错误的多种解决方案

概述 在使用Selenium和WebDriver进行网页自动化时,可能会遇到各种错误。其中一个常见问题是执行格式错误(Errno 8 Exec format error)。这个错误通常在运行ChromeDriver时出现,错误提示涉及路径中的某个文件,如THIRD_…

WordPress建站教程:零基础新手小白如何B2B外贸建站

如果你想通过独立站拿询盘获取订单,那么必须先要有一个自己的网站,所以建站是你绕不过去的一件事。对于零基础的新手小白来说,如何从零开始搭建一个B2B外贸网站呢?本文将为你提供一份详细的WordPress建站教程,帮助你从…

NodeJS云观影应用系统-计算机毕业设计源码60580

摘 要 随着互联网与移动互联网迅速普及,网络上的电影娱乐信息数量相当庞大,人们对获取感兴趣的电影娱乐信息的需求越来越大,个性化的基于Vue框架实现云观影应用系统成为一个热门。然而电影信息的表示相当复杂,因此需要开发一个基于Vue框架实…

Navicat for MySQL 17 实现更明智的业务决策

5 月 12 日,Navicat 对现有产品进行了几项重大更新,包括 Navicat Premium、Navicat BI 和 Navicat Data Modeler。最受欢迎的 Navicat 工具之一,Navicat for MySQL,也得到了新的更新,获得了许多与 Navicat Premium 相同…

PostgreSQL——查询扫描介绍

顺序扫描 概述 顺序扫描(Sequential Scan)是PostgreSQL中一种基本的数据检索方式,它通过按顺序读取表中的所有页面来查找满足查询条件的记录。这种方式不依赖于索引,因此在某些情况下可能是唯一的选择,尤其是当表没有…

熵权法确定权重

熵权法(Entropy Weight Method, EWM)是一种在综合考虑各因素提供信息量基础上计算综合指标的数学方法,属于客观综合定权法,在确定权重时更有说服力。该方法主要根据各指标传递给决策者的信息量大小来确定权重。在信息论中&#xf…