1. 程式人生 > >簡化實現動態行列轉置的SQL

簡化實現動態行列轉置的SQL

文件名 一個 ont 計算 post all .exe nts 難了

動態行列轉換的計算在實際業務中非經常見,網上各類技術論壇上都有討論,比方以下這些問題:

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