動態行列轉換的計算在實際業務中非常常見,網上各種技術論壇上都有討論,比方如下這些問題:sql
http://www.iteye.com/problems/87788數據庫
http://bbs.csdn.net/topics/390869577函數
http://bbs.csdn.net/topics/391000711工具
http://bbs.csdn.net/topics/391001035post
http://bbs.csdn.net/topics/390888703spa
http://bbs.csdn.net/topics/391012377.net
http://bbs.csdn.net/topics/390956910對象
http://bbs.csdn.net/topics/391004719blog
http://bbs.csdn.net/topics/390946260排序
http://bbs.csdn.net/topics/390937222?page=1#post-398564938
http://bbs.csdn.net/topics/390883416
http://bbs.csdn.net/topics/390960953
http://bbs.csdn.net/topics/390959646
行轉列使用SQL完畢通常有下面幾種方法:
一、 使用行列轉換函數
Oracle11g及以上和MSSQL2005+提供了行列轉置運算符pivot和unpivot,前者用於行轉列,後者用於列轉行。使用時需要指定目標列,對於動態列的場景沒法直接完畢。
二、 使用CASE表達式
對於不支持pivot的數據庫。如Mysql、DB2。可以使用case when條件表達式完畢。與pivot相似,需要依據目標列固定寫死,沒法直接寫出動態列結構轉換。
對於動態列的狀況。僅僅能:
三、 拼接動態SQL
處理動態行列轉換時每每需要在存儲過程當中拼接動態SQL完畢,由於數據庫間的差別,寫法與難易程度也不盡一樣。沒法編寫通用的SQL語句。
實際狀況中中,行列轉換每每還伴隨列間計算。增大了轉置時的難度。
行列轉換的目的常常是爲了進一步的數據呈現。也就是說會有個主程序(如報表工具等)接受結果以進行下一步操做。假設是Java主程序,則可以使用潤乾集算器(免費版)來協助完畢這類轉換。集算器是動態解釋運行的腳本。完畢行列轉換的代碼更具通用性。
集算器提供了JDBC接口,可以置於Java應用程序與數據庫之間。讓應用程序繼續象訪問數據庫同樣運行集算器腳本,不用改變應用結構。
如下以一個簡單的樣例說明用集算器怎樣實現行列轉換。並集成進Java主程序中。
通常的行轉列僅僅簡單地將數據行轉爲結果列。不涉及複雜的列間計算。
如將如下的學生成績錶轉爲分科目展現的集合:
目標結果:
實現腳本:
A1:運行SQL取數,並按ID、SUBJECT排序;
A2-A3:按ID和SUBJECT分組,集算器保留了分組後的子集供後面計算使用;
A4:動態建立空的目標結果集;
A5-B5:循環A2的學生分組,依據SUBJECT分組將學生ID、姓名和各科目成績寫入結果集;
A6:返回結果集。
從上面代碼可以看出採用集算器實現行轉列的基本步驟:先動態計算出空的目標結果集(A4)。再計算出每行數據追加到結果集中(A5,B5)。在有了支持數據表對象的分步計算機制後,行轉列的過程可以按天然思路編寫出來。
集算腳本的計算結果可以用JDBC接口返回給JAVA主程序或報表工具,JAVA調用集算腳本代碼:
Class.forName("com.esproc.jdbc.InternalDriver");
con=DriverManager.getConnection("jdbc:esproc:local://");
//調用集算器腳本(相似存儲過程),當中p1是集算器腳本文件名稱
st=(com. esproc.jdbc.InternalCStatement)con.prepareCall("call p1 ()");
//運行腳本
st.execute();
//獲取結果集
ResultSetrs = st.getResultSet();
……
返回值是符合JDBC標準的ResultSet對象,調用集算器腳本和訪問數據庫的方法全然同樣,熟悉JDBC的程序猿可以很是快掌握。
關於集算器JDBC的部署和調用的更具體信息可參考【集算器集成應用之被JAVA調用】。
要求依據最大的機組分組長度決定轉換後的結果列數。目標結果:
實現腳本:
A1:運行sql從產量表中取數;
A2:按機組分組。在集算器中分組結果保留了分組結果(成員)以方便興許使用和計算;
A3:求分組中最大成員個數,以肯定結果集列數;
A4-A5:動態建立空結果集;
A6-B7:循環A2中分組結果。將每個分組中的類別和產量寫入A5結果序表中。
與上述相似。這段代碼仍然是先動態生成空結果集。而後再計算出合適的數據追加。
本例的計算需要寫出動態的SQL來拼出結果集,但由於要找出最大的組才知道列數。拼結果也不是像通常的pivot那樣可以用字段值直接相應成列。這就要寫存儲過程一步步地完畢才方便。
相對照較複雜的存儲過程,集算腳本支持過程性計算,代碼更加簡潔、易編寫。
如開始提到的,行列轉換的同一時候每每伴隨列間計算,好比有數據:
要求依據指定年份(如2014),輸出每個月應付金額,若無當月數據,則當月應付金額爲上月該值。
目標結果:
實現腳本:
A1:運行SQL取查詢年數據;
A2:生成帶有12個月的結果空序表;
A3:按客戶分組;
A4-B7:循環分組。B5設置對應月份的應付金額。B6將空值置爲前一個月的數值。B7將記錄插入結果序表中。
運算過程仍然是先產生空結果集後追加數據,不一樣的是。這裏要追加的數據需要經常一系列計算才幹獲得。
集算腳本支持有序運算。因此很是easy取到前一條記錄的值。對於動態行列轉換時發生的列間計算。與複雜SQL或存儲過程相比。集算腳本更清晰易懂。
目標結果:
實現腳本:
A1:運行SQL取數;
A2:建立目標結果空序表;
A3:依據A1集合的列數計算每條記錄要拆分的行數;
A4-B4:循環A1集合,動態獲取每列數據插入A2結果序表中。