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字節這個問題還一直沒解決。我在想有沒有一種數據類型,我可以往里面想放多少字節就可以放多少字節的呢。。歡迎各位高手指導!希望可以幫得上遇到該問題的或者是即將遇到該問題的朋友們。

2012-6-18 16:59:51

 

 

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM