zoukankan      html  css  js  c++  java
  • Oracle行列转换的思考与总结

    最近几天一直在弄Oracle-SQL的问题,涉及到了一些平时没有用到的东西,也因此而在这里郁闷了好久。现在问题得到了解决虽说不算完美。但是还是和大家一起分享一下。

    行列转换之一:sum(case when.. then.. else.. end) as 语句

    这种也可能是我们遇到的第一个行列转换的方法。巧妙的利用汇总和判断语句就可以解决的。

    先看一个简单的基础表:如下图

    create table STUDENT
    (
      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递归 语句

    素材还是上面的表,下面我想得到的结果是:

    把一个对象的各个学科的属性连接起来放入到一个列中,用字符串存放。

    当时看到这个业务需求的时候,当然学生这个只是举个例子。很没有头绪。但是业务那边也急着要数据,没办法。只好硬着头皮想办法。首先就想起了这个要用连接字符串的函数,还需要判断循环之类的。于是乎就想起了这样做,创建一个自定义函数。来处理字符串不断的相加。

    方法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

    要说明的是:

    所以在使用这个函数之前,我们必须先对源数据进行处理。第一步简单的处理一下。

    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
               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函数自定义。

    比较复杂一点的行列转换用以上两个方法都可以实现。但是这两个方法却都存在着一些优点和缺点。但是本人建议还是使用下面的方法比较靠谱。

    虽然结合上面的各种方法解决了这个问题,但是Oracle中字符类型值不能大于4000字节这个问题还一直没解决。我在想有没有一种数据类型,我可以往里面想放多少字节就可以放多少字节的呢。。欢迎各位高手指导!希望可以帮得上遇到该问题的或者是即将遇到该问题的朋友们。

  • 相关阅读:
    Class文件和JVM的恩怨情仇
    详解及对比创建线程的三种方式
    浅析Java中线程组(ThreadGroup类)
    简单定义多线程!
    五分钟看懂UML类图与类的关系详解
    LeetCode刷题--14.最长公共前缀(简单)
    LeetCode刷题--13.罗马数字转整数(简答)
    动态规划算法详解及经典例题
    LeetCode--9.回文数(简单)
    LeetCode刷题--7.整数反转(简单)
  • 原文地址:https://www.cnblogs.com/shengs/p/4089273.html
Copyright © 2011-2022 走看看