热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

【转】oracle行转列通用过程

oracle行转列通用过程(转)环境oracle10g工作关系,常做些行转列报表,报表通常不是在大数据集合上处理.所以写了个过程.本过程比较适合在于需要动态输出报表的地方,例如web中.不是很完美

oracle行转列通用过程(转)

环境oracle 10g
工作关系,常做些行转列报表,报表通常不是在大数据集合上处理.
所以写了个过程.
本过程比较适合在于需要动态输出报表的地方,例如web中.

不是很完美,但已经可以解决绝大部分的问题.
create or replace function func_RowToCol(
viewName Varchar2,
grpCols Varchar2,
colCol Varchar2,
valueCol Varchar2,
fillEmptyWithZero Number:=1,
rowOrder Varchar2:='',
colOrder Varchar2:='',
rowOrderinGrp Integer:=1,
colOrderStyle Varchar2:=' asc ',
fillValue Varchar2:=''
) return varchar2
Is
/*****************************************************************************************************
名称:func_RowToCol

参数说明:
viewName 视图名称,实际上可以是数据库的表格名称,视图名称,也可以是SQL语句.

grpCols 需要分组的列,以格式 col1,col2..coln传入,其中n是大于0的整数
colCol 由行转为列的那个列
valueCol 行转列后,依然作为值填充的那个列,只能是一个列
--viewIsSql 视图是否是sql语句,如果是则传入1,反之传入2,默认是1(是sql)
fillEmptyWithZero 用0来填充空值,默认空值依然保留空值.如果是1,则只对valueCol为数值类型的有效.
rowOrder 结果默认的排序语句,如果有,则使用这个,这个是对结果的行排序
colOrder 对转成的列进行排序的依据.
rowOrderinGrp 行的排序列是否在分组列(grpcols)中,0 表示不是,1表示是,默认是在分组列中。
colOrderStyle 这个参数说明了列的排序方式
fillValue 填充值,如果非空,且fillEmptyWithZero=1,则用.

举例:有一个表格EmpSalary(SalMonth number,EmpName varchar2(20),salary number) 其中
salMonth,EmpName组成唯一约束
假设有以下数据:
SALMONTH EMPNAME SALARY
---------- -------------------- ----------
200801 lzf 8000
200801 wth 8000
200801 lxl 7000
200801 fjl 8000
200801 wcl 40000
200802 lzf 9000
200802 wth 8000
....
现在需要按照这样的格式输出
salaryMonth lzf wth lxl fjl wcl
200801 8000 8000 7000 8000 40000
200801 9000 8000

那么参数应该这样传递func_RowToCol('empsalary','','salarymonth','empname','salary',0,1);
输出:
如果成功,则返回一个基于tempdata_manycols的查询sql字符串
如果失败,则返回空值.

注意事项:
本函数是基于一个叫tempdata_manyCols的全局临时表处理的.
并且有以下假设:
1)固定列加动态列不超过300列
2)原来的一个列(只能有一个列)作为行内容填充的新形成后的表格中.
3)只能处理数字或者字符的信息,如果是字符,不能超过2000个.但本函数的colCol的值不应该超过30个B.
因为太长的话,行转列就没有意义了(根本没有办法看),同时oracle也不支持超过30B的列名
4)********************************不建议的事情***********************************************
*不建议对一个巨大的记录集合进行行专列操作,否则可能效率之低下是难于想象
*盖因为行转列通常用于编写报表之用.
*也严重不建议,传入的视图是基于一个很耗时的复杂查询
*最后,如果您的sql大于32K左右,本过程无能为力!
*******************************************************************************************

5)严重警告:数据源必须有数据,其次分组列应该都有数据,
6)不接受需要把数据聚集之后再显示的数据,最好自行先聚集.

修改记录:
------------------------------------------------------------
2009-03-11 lzf 新增
2009-03-12 lzf 完成初稿,可以在简单代码上测试成功.
2009-03-27 lzf 增加一个控制转换列输出的功能 colOrder
2009-04-01 lzf 修改,以便更完善
2009-04-07 lzf 修改,增加了列排序的方式,为了节约时间,不再调整参数顺序.
增加了一个填充值,以便按照要求来填充需要的内容.
*****************************************************************************************************
*/
Pragma Autonomous_Transaction;

vResultsql varchar2(32767):='';

-----处理临时数据的变量
vDatas type_str_arrs:=type_str_arrs();

vDealRows pls_integer:=0;
----最终存放数据的地方
vColNames type_str_arr:=type_str_arr(); --列名数组
vColAmount pls_integer:=0; --列的个数
vRowNames type_str_arr:=type_str_arr(); --行的内容

vGrpColAmount pls_integer:=0; --分组字段的个数,即grpCols的字段个数.
--vStarColPos pls_integer:=1; --返回的起点列标号,默认是1,但是如果行分组列不在其中,则从2开始
vSortAmount pls_integer:=0;

vIntoSqls Varchar2(32767):=''; --用于存储插入到tempdata_manycols的into脚本
vJoinCols Varchar2(32767):=''; --插入和分组的字段
vOrderCols Varchar2(32767):=''; --排序的字段
vOrderCols2 Varchar2(32767):='';

vRecordAmount pls_integer:=0; --原始数据记录数

--填充值
vFillValue Varchar2(1000):='';

v_sSql Varchar2(32767);


Function getGrpColAmount(pGrpCols In Varchar2,vRows In Out type_str_arr) Return Pls_Integer
Is
/*
本函数的作用:计算分组字段的个数
*/

Begin
--一个自定义的函数,用于把用特定符号隔开的字符串分解到一个字符串数组中.
pkg_bit.SpilitStr(pGrpcols,',',vRows);
Return vRows.Count;
End;

Function getJoinSql(pGrpcols In Varchar2) Return Varchar2
Is
/*
本函数的作用:返回join中的条件,已经假定,两个表一定是x,y
*/
vCols type_str_arr:=type_str_arr();
vResult Varchar2(32767):='';
vColName Varchar2(30):='' ; --列名
Begin
--一个自定义的函数,用于把用特定符号隔开的字符串分解到一个字符串数组中.
pkg_bit.SpilitStr(pGrpcols,',',vCols);
For i In 1..vCols.Count Loop
vColName:=vCols(i);
If i=1 Then
vResult:='y.'||vColName||'=x.'||vColName;
Else
vResult:=vResult||' and y.'||vColName||'=x.'||vColName;
End If;
End Loop;
Return vResult;
End;


Function getIntoSql(pGrpCols In Varchar2,pColCol In Varchar2:='',pRowOrderCol In Varchar2:='') Return Varchar2
Is
/*
本函数的作用:根据分组字段和转列来决定插入到临时表中所需要的子句sql
pColCol目前是没有什么意义存在的.
*/
vTempStr Varchar2(32767):=pGrpCols||','||Case When pColCol Is Not Null Then pColCol||',' Else '' End;
vPos Pls_Integer;
vAmount Pls_Integer:=0;
vResult Varchar2(32767):='';
Begin
--行排序列放在首位.
vTempstr:=Case When pRowOrderCol Is Not Null Then pRowOrderCol||',' Else '' End ||vTempstr;

vPos:=instr(vTempStr,',');
While vpos>0 Loop
vAmount:=vAmount+1;
--组成输出字段
If vAmount=1 Then
vResult:='C'||to_char(vAmount);
Else
vResult:=vResult||',C'||to_char(vAmount);
End If;
vTempStr:=substr(vTempStr,vpos+1);
vPos:=instr(vTempStr,',');
End Loop;
Return vResult;

End;

Function getOrderSql(pGrpCols In Varchar2) Return Varchar
Is
/*
本函数的作用:组成排序字段.
*/
Begin
Return getIntoSql(pGrpCols,'');
End;



Begin
--0)获得分组字段的个数
vGrpColAmount:=getGrpColAmount(grpCols,vRowNames);

--1)获得转为列之后的列名,列个数
v_sSql:='
Select Distinct '||colCol||' from ( '||viewName||' ) order by '||colCol;
Execute Immediate v_sSql Bulk Collect Into vColNames;
vColAmount:=vColNames.Count;


--2)把数据填充到临时数组表中

If colOrder Is Not Null Then --是用这个.
vOrderCols2:=' x.'||replace(grpCols,',',',x.')||',x.'||colOrder||colOrderStyle;
Else
vOrderCols2:= ' x.'||replace(grpCols,',',',x.')||',x.'||colCol||colOrderStyle;
End If;

--计算填充内容
If fillEmptyWithZero=1 And fillValue Is Not Null Then
vFillValue:=fillValue;
Elsif fillEmptyWithZero=1 And fillValue Is Null Then
vFillValue:='0';
Elsif fillEmptyWithZero=0 Then
vFillValue:='';
End If;


Execute Immediate 'truncate table tempdata_manyCols';
vJoinCols:=getJoinSql(grpCols);
v_sSql:='
select type_str_arr('||Case When rowOrderinGrp=0 Then 'x.'||rowOrder||','
Else '' End||'x.'||replace(grpCols,',',',x.')||',x.'||colCol||','||
Case When vFillValue Is Not Null Then 'nvl('||valueCol||','''||vFillValue||''') '
Else valueCol
End ||') from
(
select a.*,b.* from
(Select Distinct '||colCol||Case When colOrder Is Null Then '' Else ','||colOrder End||' from ('||viewName||')) a,
(select distinct '||grpCols||Case When rowOrderinGrp=0 Then ','||rowOrder Else '' End||' from ('||viewName||')) b
) x
left join ('||viewName||') y on '||vJoinCols||' and y.'||colCol||'=x.'||colCol||'
order by '||vOrderCols2;
Execute Immediate v_sSql Bulk Collect Into vDatas;
Commit;

--3)通过矩阵转换,读取vDatas的内容,插入到tempdata_manycols
vRecordAmount:=vDatas.Count;
--设置插入列的SQL
If rowOrderinGrp=1 Then --如果排序行的列在分组列中,那么就仅仅使用分组列即可.
vIntoSqls:=getIntoSql(grpCols);
Else --如果行的排序列不在分组列中,那么就再多插入一个列.
vIntoSqls:=getIntoSql(grpCols,'',rowOrder);
vSortAmount:=1;
End If;

/*组合sql
*/
vDealRows:=0;
For i In 1..vRecordAmount Loop --每一行
If Mod(i,vColAmount)=0 Then --当读取到足够一行数据的时候
vDealRows:=vDealRows+1;
If vDealRows=1 Then --为了不至于搞错顺序,第一次需要把转列内容填到
--vColNames,也就是例子中的 lzf,wth...等部分
--同时,组合成真正的intosql
For j In 1 ..vColAmount Loop
--vsortAmount表示的是行排序列的个数.
vColNames(j):=vDatas(j)(vGrpColAmount+vSortAmount+1);
--dbms_output.put(lpad( vColNames(j-(vDealRows-1)*vColAmount),20,' '));
vIntoSqls:=vIntoSqls||',C'||to_char(vGrpColAmount+vSortAmount+j);
End Loop;

End If;

v_sSql:='';
For x In 1..vGrpColAmount+vSortAmount Loop --设置值部分分组部分的内容和排序部分内容
v_sSql:=v_sSql||Case x When 1 Then '' Else ',' End||''''||vDatas(i)(x)||'''';
End Loop;
--读取值部分填充值的信息
--真正的值的位置=分组字段个数+排序列个数+2
For j In (vDealRows-1)*vColAmount+1..vDealRows*vColAmount Loop
v_sSql:=v_sSql||','||Case When vDatas(j)(vGrpColAmount+vSortAmount+ 2) Is Null Then 'null'
Else ''''||vDatas(j)(vGrpColAmount+vSortAmount+2)||''''
End;
--dbms_output.put(lpad( vDatas(j)(vGrpColAmount+2),20,' '));
End Loop;
--dbms_output.put_line('');

--组合成最后的SQL
v_sSql:=' insert into tempdata_manycols('||vIntoSqls||') values('
||v_sSql||')';
--dbms_output.put_line(v_sSql);
Execute Immediate v_sSql;
End If;
End Loop;


Commit;
--4)形成返回的sql
/*
关键在于知道列名:=分组名称+行转列
*/
/* If rowOrder Is Not Null And rowOrderinGrp=0 Then
vStarColPos:=2;
End If;*/
vResultsql:='select ';
--分别是排序列,和转换列
For i In 1..vGrpColAmount+vColAmount Loop
If i<=vgrpcolamount Then --前一部分的字段别名是分组字段名称,是外部传入的.
vResultsql:=vResultsql||'
'||(Case i When 1 Then '' Else ',' End)||'C'||(i+vSortAmount)||' as '||vRowNames(i);
Else --后面一部分则是由原来行转过来的列的别名.
vResultsql:=vResultsql||'
'||(Case i When 1 Then '' Else ',' End)||'C'||(i+vSortAmount)||' as "'||vColNames(i-vGrpColAmount)||'"';
End If;
End Loop;
If rowOrder Is Null Then
vOrderCols:=getordersql(grpCols);
Else
--vOrderCols:=rowOrder;
--默认只有一个的情况下.正确的情况,应该是另外处理。
vOrderCols:=' c1 ';
End If;
vResultsql:=vResultsql||'
from tempdata_manycols
order by '||vOrderCols;
return(vResultsql);
Exception
When Others Then
Rollback;
Return '';
end func_RowToCol;



测试如下:
SQL> select * from empsalary;

SALMONTH EMPNAME SALARY COUNTRY
---------- -------------------- ---------- --------------------
200801 lzf 8000 中国
200801 wth 8000 美国
200801 lxl 7000 日本
200801 fjl 7000 巴基斯坦
200801 wcl 40000 美国
200802 lzf 9000 中国
200802 wth 8000 美国
200802 lxl 8500 日本

8 rows selected

SQL> Select func_RowToCol(' empsalary ','salmonth,country','empname','salary') From dual;

FUNC_ROWTOCOL('EMPSALARY','SAL
--------------------------------------------------------------------------------
select
C1 as salmonth
,C2 as country
,C3 as "fjl"
,C4 as "lxl"
,C5 as "lzf"
,C6 as "wcl"
,C7 as "wth"
from tempdata_manycols
order by C1,C2


SQL> select
2 cast(C1 As Varchar2(10)) as salmonth
3 ,cast(C2 As Varchar2(10)) as country
4 ,cast(C3 As Varchar2(10)) as "fjl"
5 ,cast(C4 As Varchar2(10)) as "lxl"
6 ,cast(C5 As Varchar2(10)) as "lzf"
7 ,cast(C6 As Varchar2(10)) as "wcl"
8 ,cast(C7 As Varchar2(10)) as "wth"
9 from tempdata_manycols
10 order by C1,C2
11 /

SALMONTH COUNTRY fjl lxl lzf wcl wth
---------- ---------- ---------- ---------- ---------- ---------- ----------
200801 巴基斯坦 7000 0 0 0 0
200801 美国 0 0 0 40000 8000
200801 日本 0 7000 0 0 0
200801 中国 0 0 8000 0 0
200802 美国 0 0 0 0 8000
200802 日本 0 8500 0 0 0
200802 中国 0 0 9000 0 0

7 rows selected

推荐阅读
  • ASP.NET2.0数据教程之十四:使用FormView的模板
    本文介绍了在ASP.NET 2.0中使用FormView控件来实现自定义的显示外观,与GridView和DetailsView不同,FormView使用模板来呈现,可以实现不规则的外观呈现。同时还介绍了TemplateField的用法和FormView与DetailsView的区别。 ... [详细]
  • Oracle优化新常态的五大禁止及其性能隐患
    本文介绍了Oracle优化新常态中的五大禁止措施,包括禁止外键、禁止视图、禁止触发器、禁止存储过程和禁止JOB,并分析了这些禁止措施可能带来的性能隐患。文章还讨论了这些禁止措施在C/S架构和B/S架构中的不同应用情况,并提出了解决方案。 ... [详细]
  • Android自定义控件绘图篇之Paint函数大汇总
    本文介绍了Android自定义控件绘图篇中的Paint函数大汇总,包括重置画笔、设置颜色、设置透明度、设置样式、设置宽度、设置抗锯齿等功能。通过学习这些函数,可以更好地掌握Paint的用法。 ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • Oracle10g备份导入的方法及注意事项
    本文介绍了使用Oracle10g进行备份导入的方法及相关注意事项,同时还介绍了2019年独角兽企业重金招聘Python工程师的标准。内容包括导出exp命令、删用户、创建数据库、授权等操作,以及导入imp命令的使用。详细介绍了导入时的参数设置,如full、ignore、buffer、commit、feedback等。转载来源于https://my.oschina.net/u/1767754/blog/377593。 ... [详细]
  • Windows7 64位系统安装PLSQL Developer的步骤和注意事项
    本文介绍了在Windows7 64位系统上安装PLSQL Developer的步骤和注意事项。首先下载并安装PLSQL Developer,注意不要安装在默认目录下。然后下载Windows 32位的oracle instant client,并解压到指定路径。最后,按照自己的喜好对解压后的文件进行命名和压缩。 ... [详细]
  • 在Oracle11g以前版本中的的DataGuard物理备用数据库,可以以只读的方式打开数据库,但此时MediaRecovery利用日志进行数据同步的过 ... [详细]
  • 本文介绍了Oracle存储过程的基本语法和写法示例,同时还介绍了已命名的系统异常的产生原因。 ... [详细]
  • 从Oracle安全移植到国产达梦数据库的DBA实践与攻略
    随着我国对信息安全和自主可控技术的重视,国产数据库在党政机关、军队和大型央企等行业中得到了快速应用。本文介绍了如何降低从Oracle到国产达梦数据库的技术门槛,保障用户现有业务系统投资。具体包括分析待移植系统、确定移植对象、数据迁移、PL/SQL移植、校验移植结果以及应用系统的测试和优化等步骤。同时提供了移植攻略,包括待移植系统分析和准备移植环境的方法。通过本文的实践与攻略,DBA可以更好地完成Oracle安全移植到国产达梦数据库的工作。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • 本文讨论了在VMWARE5.1的虚拟服务器Windows Server 2008R2上安装oracle 10g客户端时出现的问题,并提供了解决方法。错误日志显示了异常访问违例,通过分析日志中的问题帧,找到了解决问题的线索。文章详细介绍了解决方法,帮助读者顺利安装oracle 10g客户端。 ... [详细]
  • Java 11相对于Java 8,OptaPlanner性能提升有多大?
    本文通过基准测试比较了Java 11和Java 8对OptaPlanner的性能提升。测试结果表明,在相同的硬件环境下,Java 11相对于Java 8在垃圾回收方面表现更好,从而提升了OptaPlanner的性能。 ... [详细]
  • 十大经典排序算法动图演示+Python实现
    本文介绍了十大经典排序算法的原理、演示和Python实现。排序算法分为内部排序和外部排序,常见的内部排序算法有插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。文章还解释了时间复杂度和稳定性的概念,并提供了相关的名词解释。 ... [详细]
  • 本文详细介绍了使用 SQL Load 和 Excel 的 Concatenate 功能将数据导入 ORACLE 数据库的方法和步骤,同时介绍了使用 PL/SQL tools 将数据导入临时表的方法。此外,还提供了一个转链接,可参考更多相关内容。摘要共计XXX字。 ... [详细]
  • MySQL数据库锁机制及其应用(数据库锁的概念)
    本文介绍了MySQL数据库锁机制及其应用。数据库锁是计算机协调多个进程或线程并发访问某一资源的机制,在数据库中,数据是一种供许多用户共享的资源,如何保证数据并发访问的一致性和有效性是数据库必须解决的问题。MySQL的锁机制相对简单,不同的存储引擎支持不同的锁机制,主要包括表级锁、行级锁和页面锁。本文详细介绍了MySQL表级锁的锁模式和特点,以及行级锁和页面锁的特点和应用场景。同时还讨论了锁冲突对数据库并发访问性能的影响。 ... [详细]
author-avatar
海角处回忆_417
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有