簡化實現動態行列轉置的SQL
動態行列轉換的計算在實際業務中非經常見,網上各類技術論壇上都有討論,比方以下這些問題:
http://www.iteye.com/problems/87788
http://bbs.csdn.net/topics/390869577
http://bbs.csdn.net/topics/391000711
http://bbs.csdn.net/topics/391001035
http://bbs.csdn.net/topics/390888703
http://bbs.csdn.net/topics/391012377
http://bbs.csdn.net/topics/390956910
http://bbs.csdn.net/topics/391004719
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完畢一般有下面幾種方法:
1、 使用行列轉換函數
Oracle11g及以上和MSSQL2005+提供了行列轉置運算符pivot和unpivot,前者用於行轉列,後者用於列轉行。使用時須要指定目標列,對於動態列的場景無法直接完畢。
2、 使用CASE表達式
對於不支持pivot的數據庫。如Mysql、DB2。能夠使用case when條件表達式完畢。與pivot類似,須要依據目標列固定寫死,無法直接寫出動態列結構轉換。
對於動態列的情況。僅僅能:
3、 拼接動態SQL
處理動態行列轉換時往往須要在存儲過程中拼接動態SQL完畢,因為數據庫間的差異,寫法與難易程度也不盡同樣。無法編寫通用的SQL語句。
實際情況中中,行列轉換往往還伴隨列間計算。增大了轉置時的難度。
行列轉換的目的經常是為了進一步的數據呈現。也就是說會有個主程序(如報表工具等)接受結果以進行下一步操作。假設是Java主程序,則能夠使用潤乾集算器(免費版)來協助完畢這類轉換。集算器是動態解釋運行的腳本。完畢行列轉換的代碼更具通用性。
集算器提供了JDBC接口,能夠置於Java應用程序與數據庫之間。讓應用程序繼續象訪問數據庫一樣運行集算器腳本,不用改變應用結構。
以下以一個簡單的樣例說明用集算器怎樣實現行列轉換。並集成進Java主程序中。
1、簡單的行轉列
一般的行轉列僅僅簡單地將數據行轉為結果列。不涉及復雜的列間計算。
如將以下的學生成績表轉為分科目展示的集合:
目標結果:
實現腳本:
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調用】。
2、不定長分組的行轉列
上一個樣例中,結果集的列(即科目)常常能夠事先獲知,這樣用靜態的pivot(或case when)語法寫出來也不算非常困難。但假設結果集的列須要動態計算出來,用pivot就非常困難了。如本例中每類機制生產的產品列數不定:
要求依據最大的機組分組長度決定轉換後的結果列數。目標結果:
實現腳本:
A1:運行sql從產量表中取數;
A2:按機組分組。在集算器中分組結果保留了分組結果(成員)以方便興許使用和計算;
A3:求分組中最大成員個數,以確定結果集列數;
A4-A5:動態創建空結果集;
A6-B7:循環A2中分組結果。將每一個分組中的類別和產量寫入A5結果序表中。
與上述類似。這段代碼仍然是先動態生成空結果集。然後再計算出合適的數據追加。
本例的計算須要寫出動態的SQL來拼出結果集,但因為要找出最大的組才知道列數。拼結果也不是像一般的pivot那樣能夠用字段值直接相應成列。這就要寫存儲過程一步步地完畢才方便。
相對照較復雜的存儲過程,集算腳本支持過程性計算,代碼更加簡潔、易編寫。
3、包括列間計算的行轉列
如開始提到的,行列轉換的同一時候往往伴隨列間計算,比如有數據:
要求依據指定年份(如2014),輸出每月應付金額,若無當月數據,則當月應付金額為上月該值。
目標結果:
實現腳本:
A1:運行SQL取查詢年數據;
A2:生成帶有12個月的結果空序表;
A3:按客戶分組;
A4-B7:循環分組。B5設置對應月份的應付金額。B6將空值置為前一個月的數值。B7將記錄插入結果序表中。
運算過程仍然是先產生空結果集後追加數據,不同的是。這裏要追加的數據須要常常一系列計算才幹得到。
集算腳本支持有序運算。所以非常easy取到前一條記錄的值。對於動態行列轉換時發生的列間計算。與復雜SQL或存儲過程相比。集算腳本更清晰易懂。
4、列轉行
除了上述提到的轉置,有時還有將一行多列數據轉為多行數據(列轉行)。例如以下數據,當中列數不定:
目標結果:
實現腳本:
A1:運行SQL取數;
A2:創建目標結果空序表;
A3:依據A1集合的列數計算每條記錄要拆分的行數;
A4-B4:循環A1集合,動態獲取每列數據插入A2結果序表中。
簡化實現動態行列轉置的SQL