Oracle行列互换

来源:互联网 发布:q叔淘宝店铺网址 编辑:程序博客网 时间:2024/05/05 23:04
近几天一直在弄Oracle-SQL的问题,涉及到了一些平时没有用到的东西,也因此而在这里郁闷了好久。现在问题得到了解决虽说不算完美。但是还是和大家一起分享一下。
行列转换之一:sum(case when.. then.. else.. end) as 语句
这种也可能是我们遇到的第一个行列转换的方法。巧妙的利用汇总和判断语句就可以解决的。
先看一个简单的基础表:如下图
create table STUDENT
(  www.2cto.com  
  STUNAME      NVARCHAR2(50),
  SUBJECTNAME  NVARCHAR2(50),
  SUBJECTSCORE NUMBER
)
 

 
表中有两个人的成绩,每一行代表每个学生该学科的成绩。这个也是我们初学SQL时候很常见的一个表结构了。
要求:把上面的多行值以学生为单位转化为2行值,可以很直观的看出每个学生的各科成绩。于是乎便有了下面的语句。
select  
s.stuname,
sum(case when s.subjectname='语文' then  s.subjectscore else 0 end  )  as 语文成绩,
sum(case when s.subjectname='数学' then  s.subjectscore else 0 end  )  as 数学成绩,
sum(case when s.subjectname='英语' then  s.subjectscore else 0 end  )  as 英语成绩
from student s
group by s.stuname
order by s.stuname
执行后所得结果:


 
上面的做法也很容易让人理解,在这里就不多说了,但是上面的也是有局限性的。因为此处毕竟存在着一些固定值。比如case  中的“语文,数学。。”不过遇到小的业务需求,还是可以拿来直接用的。比如一个学生成绩管理系统这个就可以满足了。
行列转换之二:sys_connect_by_path系统函数,自定义函数,connect by sname = prior sname and rank-1 = prior rank递归 语句  www.2cto.com  
素材还是上面的表,下面我想得到的结果是:


 
把一个对象的各个学科的属性连接起来放入到一个列中,用字符串存放。
当时看到这个业务需求的时候,当然学生这个只是举个例子。很没有头绪。但是业务那边也急着要数据,没办法。只好硬着头皮想办法。首先就想起了这个要用连接字符串的函数,还需要判断循环之类的。于是乎就想起了这样做,创建一个自定义函数。来处理字符串不断的相加。
方法1:自定义函数,循环
CREATE OR REPLACE FUNCTION getallsubject(parameter varchar2)--有参数方法,字符串
RETURN varchar2
IS
return_str varchar2(4000);--该方法返回一个字符串。
BEGIN
FOR rs IN 
(
SELECT  s.subjectname||':'||s.subjectscore as allshow 
FROM student s
WHERE s.stuname=parameter--当参数一直符合条件 for循环插入结果集rs
) LOOP
return_str:=return_str||rs.allshow;--loop所有字符串,相加
END LOOP;
RETURN return_str;
END;
执行select s.stuname,getallsubject(s.stuname) from  student s


 
可见,改方法对每一行值都进行了判断,产生了多条记录。然后select distinct s.stuname,getallsubject(s.stuname) from  student s
效果:


 
得到了想要的结果。
方法2:利用oracle自带的sys_connect_by_path
要说明的是:  www.2cto.com  


 
所以在使用这个函数之前,我们必须先对源数据进行处理。第一步简单的处理一下。
create table stu1 as
select s.stuname sname,s.subjectname||s.subjectscore  sshow from student s
--
select *from stu1
如下所示:


 
OK下面就可以写语句了。
select sname as 姓名,allstr 详细描述 from 
(
  select sname,allstr,
  row_number() over(partition by sname order by sname,curr_level desc) ename_path_rank
  from (
         select sname,sshow,rank,level as curr_level,
         ltrim(sys_connect_by_path(sshow,','),',') allstr from --把所有字符串相加
         (
           select s1.sname,s1.sshow,row_number() over(partition by s1.sname order by s1.sname,s1.sshow) rank  www.2cto.com  
           from stu1 s1 order by s1.sname,s1.sshow--创建树关系,name可以作为parentid,rank可以作为childid
         ) connect by sname = prior sname and rank-1 = prior rank
       )
)
where ename_path_rank=1;
执行结果:


 
也得到了我们想要的结果。当然不用学科之间的间隔符我们可以用replace函数自定义。
比较复杂一点的行列转换用以上两个方法都可以实现。但是这两个方法却都存在着一些优点和缺点。但是本人建议还是使用下面的方法比较靠谱
###################################################################################################################################
###################################################################################################################################
固定列数的行列转换

student   subject   grade   
---------------------------   
student1   语文   80   
student1   数学   70   
student1   英语   60   
student2   语文   90   
student2   数学   80   
student2   英语   100   
……   
转换为     
语文   数学   英语   
student1   80   70   60   
student2   90   80   100  

……   
语句如下:   
[sql] view plaincopy
  1. select   student,sum(decode(subject,'语文',   grade,null))   "语文",     
  2. sum(decode(subject,'数学',   grade,null))   "数学",     
  3. sum(decode(subject,'英语',   grade,null))   "英语"     
  4. from   table     
  5. group   by   student     
不固定列数行列互换

如   
c1   c2   
--------------   
1   我   
1   是   
1   谁   
2   知   
2   道   
3   不   
……

 转换为   
1   我是谁   
2   知道   
3   不   
这一类型的转换必须借助于PL/SQL来完成,这里给一个例子

[sql] view plaincopy
  1. CREATE   OR   REPLACE   FUNCTION   get_c2(tmp_c1   NUMBER)       
  2. RETURN   VARCHAR2       
  3. IS       
  4. Col_c2   VARCHAR2(4000);       
  5. BEGIN     
  6. FOR   cur   IN   (SELECT   c2   FROM   t   WHERE   c1=tmp_c1)   LOOP       
  7. Col_c2   :=   Col_c2||cur.c2;       
  8. END   LOOP;       
  9. Col_c2   :=   rtrim(Col_c2,1);     
  10. RETURN   Col_c2;       
  11. END;  
[sql] view plaincopy
  1. SQL>   select   distinct   c1   ,get_c2(c1)   cc2   from   table;  

 

看到这里,已经有了解决思路了,在oracle中建一个function,代码如下:

[sql] view plaincopy
  1. CREATE OR REPLACE FUNCTION get_ver(tmp_boinstid VARCHAR2)  
  2. RETURN   VARCHAR2  
  3. IS  
  4. vers   VARCHAR2(100);  
  5. BEGIN  
  6. FOR   cur   IN   (SELECT pln_ver FROM PMS_BUDGET_PLAN WHERE boinst_id=tmp_boinstid order by is_all_run)   LOOP  
  7. vers   :=   vers||cur.pln_ver||',';  
  8. END   LOOP;  
  9. vers   :=   rtrim(vers,1);  
  10. RETURN   vers;  
  11. END get_ver;  


然后使用这个函数来查询就可以了,

[sql] view plaincopy
  1. SELECT distinct p.pln_name,p.pln_year,get_ver(boinst_id) FROM PMS_BUDGET_PLAN p WHERE p.pln_type =20 and p.del_flag = -1  and boinst_id is not null  

需要注意的是,由于函数中也使用了sql,所以这个方法不太适合大数据量的查询,使用时应注意。


##################################################################################################################

##################################################################################################################

oracle 动态交叉查询 行列互换 oracle动态交叉表

使用视图 动态创建视图

SQL code
CREATE OR REPLACE procedure DWDEV.dw_dis_proc(tabname in varchar2,--需要进行行转列操作的表名 group_col in varchar2,--查询结果要按某列或某些列分组的字段名
 column_col in varchar2,--要从行转成列的字段 value_col in varchar2,--需要聚合的值字段 Aggregate_func in varchar2 default 'max',--选用的聚合函数,可选,默认为max condition in varchar2 default '1=1',--条件语句,可选 colorder in varchar2 default null,--行转列后列的排序,可选 roworder in varchar2 default null,--行转列后记录的排序,可选 when_value_null in varchar2 default null,--若value_col字段的值聚合后为空,则转换成该值,可选 viewname in varchar2 default 'v_tmp'--创建的视图名称,可选,默认为v_tmp) Authid Current_Userasc1 sys_refcursor;v1 varchar2(1000);sqlstr varchar2(10000);countTemp number(10); beginEXECUTE IMMEDIATE 'select count(distinct '||column_col||') from '||tabname into countTemp; sqlstr :='create or replace view '||viewname||' as select '||group_col||',' ||Aggregate_func||'('||value_col||') as "summation",round('|| Aggregate_func||'('||value_col||')/'||countTemp||',2) as "average"';open c1 for 'select distinct '||column_col||' from '||tabname||' where '||condition|| case when colorder is not null then ' order by '||colorder end; loop fetch c1 into v1;exit when c1%notfound;sqlstr:=sqlstr||chr(10)||','||case when when_value_null is not null then 'nvl(' end||Aggregate_func||'(decode(to_char('||column_col||'),'''||v1||''','||value_col||'))'||case when when_value_null is not null then chr(44) ||when_value_null||chr(41) end||'"'||v1||'"';end loop;close c1;sqlstr:=sqlstr||' from '||tabname||' where '||condition||' group by '||group_col||case when roworder is not null then ' order by '||roworder end;execute immediate sqlstr;end dw_dis_proc;
select * from v_tmp
 
################################################################################################################################
################################################################################################################################

Oracle行列互换 横表和纵表

tb_score表 

转换成:

SQL实现代码:

[sql] view plaincopyprint?
  1. create table tb_score  
  2. (  
  3.        stu_id int,  
  4.        stu_name varchar2(20),  
  5.        stu_object varchar2(10),  
  6.        stu_score number(6,2)  
  7. )  
  8. go  
  9. select * from tb_score;  
  10. go  
  11. insert into tb_score values(1,'andy','Chinese',80);  
  12. insert into tb_score values(1,'andy','Math',70.05);  
  13. insert into tb_score values(1,'andy','English',90);  
  14. insert into tb_score values(2,'DK','Chinese',80.05);  
  15. insert into tb_score values(2,'DK','Math',73.05);  
  16. insert into tb_score values(2,'DK','English',99);  
  17. go  
  18.   
  19. select stu_id,stu_name,sum(decode(stu_object,'Chinese',stu_score)) 语文,  
  20.                          sum(decode(stu_object,'Math',stu_score)) 数学,  
  21.                          sum(decode(stu_object,'English',stu_score)) 英语 from tb_score   
  22.                          group by stu_id,stu_name;  
  23. select stu_id,stu_name,sum(case stu_object when 'Chinese' then stu_score end) 语文,   
  24.                          sum(case stu_object when 'Math' then stu_score end) 数学,   
  25.                          sum(case stu_object when 'English' then stu_score end) 英语 from tb_score  
  26.                          group by stu_id,stu_name;  
  27.                                                   

上面两种方式都可以,



原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 手机被wifi禁了怎么办 母乳一边是咸的怎么办 tcl电视蓝频了怎么办 长虹电视蓝频了怎么办 电视突然蓝频了怎么办 海信电视蓝频了怎么办 连网电视蓝频了怎么办 英雄联盟画面卡顿怎么办 长残了怎么办原来很帅 被吓到了怎么办没精神 宝宝吓着怎么办最有效 4个月婴儿易惊吓怎么办 心里有问题的人怎么办 减肥的时候想吃东西怎么办 大联盟ping很高怎么办 酷派手机弹广告怎么办 孕早期肚子紧绷怎么办 怀孕2个月同房了怎么办 怀孕前三月同房了怎么办 人流前三天同房了怎么办 人流后三天同房了怎么办 吃避孕药后月经量少怎么办 排卵日同房不想怀孕怎么办 排卵日同房没有怀孕怎么办 想怀孕想生儿子怎么办 到期大姨妈不来怎么办 怕怀孕月经不来怎么办 宫颈环扎后宫缩怎么办 来例假刺激外阴高潮了怎么办 客人消费完说没钱怎么办? 孕13周血糖偏高怎么办 餐后血糖高14点怎么办 餐后血糖20多怎么办 空腹血糖6.6饭后9.2怎么办 键盘shift键失效了怎么办 灵异附身夫妻不合怎么办 鞋子买大了半码怎么办 鞋子买小了半码怎么办 黑布鞋鞋面白了怎么办 黑布鞋不黑了怎么办 黑布鞋退白了怎么办