問題介紹
作為 java 程式員,用代碼直接實作類似 SQL 中的交并補差的集合運算,總是要編寫大量的代碼,如果能有一個專門的外部資料工具,通過寫簡單類似 SQL 的腳本來實作,在 java 中直接調用并可以傳回結果集,就再好不過了。Java 版集算器正是解決這一難題的神器,通過 SPL 腳本可以直覺自然得寫出運算,再使用 java 調用 SPL 腳本,使用起來簡單,快捷,高效。另外,雖然 SQL 有集合概念,但對于有序集合運算提供的支援卻很有限,經常要采用很費解的思路才可以完成, SPL 基于離散資料集模型,能輕松處理有序集合運算。下面我們就由淺入深,舉例說明如何使用。
SPL 實作
和集
示例 1: 求重疊時間段的總天數
MySQL8:
with recursive t(start,end) as (select date'2010-01-07',date'2010-01-9'
union all select date'2010-01-15',date'2010-01-16'
union all select date'2010-01-07',date'2010-01-12'
union all select date'2010-01-08',date'2010-01-11'),
t1(d,end) as (select start,end from t
union all select d+1,end from t1 where d
select count(distinct d) from t1;
說明:此例先将各時間段轉成時間段内所有日子對應的日期,然後再求不同日期的個數
集算器 SPL:

A3: 對 A2 中的每一個時間段構造從 start 到 end 的日期序列
A4: 求 A3 中所有日期序列的和
A5: 求 A4 中不重複日期的個數
儲存腳本檔案SumSet.dfx (嵌入 Java 會用到)
差集
示例 1: 列出英語人口和法語人口均超過 5% 的國家
MySQL8:
with t1(lang) as (select 'English' union all select 'French')
select name from world.country c
where not exists(select * from t1 where lang not in (select language from world.countrylanguage
where percentage>=5 and countrycode=c.code
)
);
說明:此 SQL 隻是示範通過雙重否定實作差集為空
集算器 SPL:
A4: 選出 [“English”,”French”] 與本組語言集合的差為空的組,意思就是選出語言集合包含 English 和 French 的組
儲存腳本檔案DifferenceSet.dfx (嵌入 Java 會用到)
交集
示例 1: 列出英語人口、法語人口、西班牙語人口分别超過 0.3%、0.2%、0.1% 的國家代碼
MySQL8:
with t1 as (select countrycode from world.countrylanguage where language='English' and percentage>0.3),
t2 as (select countrycode from world.countrylanguage where language='French' and percentage>0.2),
t3 as (select countrycode from world.countrylanguage where language='Spanish' and percentage>0.1)
select countrycode
from t1 join t2 using(countrycode) join t3 using(countrycode);
說明:此例隻是示範如何求解多個集合的交集
集算器 SPL:
A3: 按次序依次查詢英語人口超 0.3%、法語人口超 0.2%、西班牙語超 0.1% 的國家代碼,并轉成序列
A5: A3 中所有序列交集
儲存腳本檔案IntersectionSet.dfx (嵌入 Java 會用到)
Java 調用
SPL 嵌入到 Java 應用程式十分友善,通過 JDBC 調用存儲過程方式加載,用和集儲存的檔案SumSet.dfx,示例調用如下:
...
Connection con = null;
Class.forName("com.esproc.jdbc.InternalDriver");
con= DriverManager.getConnection("jdbc:esproc:local://");
//調用存儲過程,其中SumSet是dfx的檔案名
st =(com. esproc.jdbc.InternalCStatement)con.prepareCall("call SumSet()");
//執行存儲過程
st.execute();
//擷取結果集
ResultSet rs = st.getResultSet();
...
替換成DifferenceSet.dfx或IntersectionSet.dfx是同樣的道理,隻需 call DifferenceSet()或者 call IntersectionSet() 即可。這裡隻用 Java 片段粗略解釋了如何嵌入 SPL,詳細步驟請參閱Java 如何調用 SPL 腳本,也非常簡單,不再贅述。同時,SPL 也支援 ODBC 驅動,內建到支援 ODBC 的語言,嵌入過程類似。
擴充節選
關于集合運算除了上面講的和差交運算,還可以擷取與行号有關的計算,以及有序集合的對位運算。
根據行号取資料
示例 1: 計算招商銀行 (600036) 2017 年第 3 個交易日和倒數第 3 個交易日的交易資訊
MySQL8:
with t as (select *, row_number() over(order by tdate) rn from stktrade where sid='600036' and tdate between '2017-01-01' and '2017-12-31')
select tdate,open,close,volume from t where rn=3
union all
select tdate,open,close,volume from t where rn=(select max(rn)-2 from t);
集算器 SPL:
示例 2: 計算招商銀行 (600036) 最近 20 個交易日的平均收盤價
MySQL8:
with t as (select *, row_number() over(order by tdate desc) rn from stktrade where sid='600036')
select avg(close) avg20 from t where rn<=20;
集算器 SPL:
求滿足條件的記錄的行号
示例 1: 計算招商銀行 (600036)2017 年經過多少交易日收盤價達到 25 元
MySQL8:
with t as (select *, row_number() over(order by tdate) rn from stktrade where sid='600036' and tdate between '2017-01-01' and '2017-12-31')
select min(rn) from t where close>=25;
集算器 SPL:
示例 2: 計算格力電器 (000651) 2017 年漲幅 (考慮停牌)
MySQL8:
with t as (select * from stktrade where sid='000651'),
t1(d) as (select max(tdate) from t where tdate<'2017-01-01'),
t2(d) as (select max(tdate) from t where tdate<'2018-01-01')
select s2.close/s1.close-1 rise
from (select * from t,t1 where tdate=d) s1,
(select * from t,t2 where tdate=d) s2;
集算器 SPL:
A2: 資料按交易日從小到大排序
A3: 從後往前查找交易日在 2017-01-01 之前的最後一條記錄在序列中的行号
A4: 求 2016 年收盤價
A5: 求 2017 年收盤價,其中 A2.m(-1) 取倒數第 1 條記錄,即 2017 年最後一個交易日對應的記錄
示例 3: 列出 2017 年資訊發展 (300469) 交易量超過 250 萬股時的交易資訊及各日漲幅(考慮停牌)
MySQL8:
with t as (select *, row_number() over(order by tdate) rn
from stktrade where sid='300469' and tdate<=date '2017-12-31'),
t1 as (select * from t where tdate>=date'2017-01-01' and volume>=2500000)
select t1.tdate, t1.close, t.volume, t1.close/t.close-1 rise
from t1 join t on t1.rn=t.rn+1;
集算器 SPL:
求最大值或最小值所在記錄的行号
示例 1: 計算招商銀行 (600036) 2017 年最早的最低價與最早的最高價間隔多少交易日
MySQL8:
with t as (select *, row_number() over(order by tdate) rn from stktrade where sid='600036' and tdate between '2017-01-01' and '2017-12-31'),
t1 as (select * from t where close=(select min(close) from t)),
t2 as (select * from t where close=(select max(close) from t))
select abs(cast(min(t1.rn) as signed)-cast(min(t2.rn) as signed)) inteval
from t1,t2;
集算器 SPL:
示例 2: 計算招商銀行 (600036) 2017 年最後的最低價與最後的最高價間隔多少交易日
MySQL8:
with t as (select *, row_number() over(order by tdate) rn from stktrade where sid='600036' and tdate between '2017-01-01' and '2017-12-31'),
t1 as (select * from t where close=(select min(close) from t)),
t2 as (select * from t where close=(select max(close) from t))
select abs(cast(max(t1.rn) as signed)-cast(max(t2.rn) as signed)) inteval
from t1,t2;
集算器 SPL:
有序集合間的對位計算
示例 1: 求 2018 年 3 月 6 日到 8 日創業闆指 (399006) 對深證成指 (399001) 的每日相對收益率
MySQL8:
with t1 as (select *,close/lag(close) over(order by tdate) rise from stktrade where sid='399006' and tdate between '2018-03-05' and '2018-03-08'),
t2 as (select *, close/lag(close) over(order by tdate) rise from stktrade where sid='399001' and tdate between '2018-03-05' and '2018-03-08')
select t1.rise-t2.rise
from t1 join t2 using(tdate)
where t1.rise is not null;
集算器 SPL:
SPL 優勢
有庫寫 SQL,沒庫寫 SPL
用 Java 程式直接彙總計算資料,還是比較累的,代碼很長,并且不可複用,很多情況資料也不在資料庫裡,有了 SPL,就能像在 Java 中用 SQL 一樣了,十分友善。
常用無憂,不花錢就能取得終身使用權的入門版
如果要分析的資料是一次性或臨時性的,潤乾集算器每個月都提供免費試用授權,可以循環免費使用。但要和 Java 應用程式內建起來部署到伺服器上長期使用,定期更換試用授權還是比較麻煩,潤乾提供了有終身使用權的入門版,解決了這個後顧之憂,獲得方式參考 如何免費使用潤乾集算器?
技術文檔和社群支援
官方提供的集算器技術文檔本身就有很多現成的例子,正常問題從文檔裡都能找到解決方法。如果獲得了入門版,不僅能夠使用 SPL 的正常功能,碰到任何問題都可以去乾學院上去咨詢,官方通過該社群對入門版使用者提供免費的技術支援。