SPL 簡化 SQL 案例詳解:分組關聯

在數據庫應用開發中,我們經常需要面對複雜的SQL式計算,比如多層分組中的關聯計算。在SQL中,分組必須同時進行彙總計算,並且不能進行對象式關聯訪問,因此處理這類問題會比較複雜,只能用窗口函數嵌套多層子查詢這類高級技巧來實現。而本文要介紹的SPL能夠支持真正的分組,進行直觀的對象式關聯訪問,從而解決這類問題更加容易。

分組關聯在實際業務中遇到的很多,下面以實際業務爲藍本設計一個比較通用的例子,以此說明SPL實現分組關聯的具體過程:

計算目標:查詢出缺貨的DVD分店,即現存的DVD拷貝不到4類的分店。

數據結構

l  Branch表,存儲DVD分店信息;

l  DVD表,存儲DVD的標題及分類信息,DVD是虛擬的數據,比如“變形金剛4”是一個DVD,但它不是一張可見的光盤

l  DVDCopy表,存儲DVD的多張拷貝,DVD拷貝是真正的光盤,以實體形式存放於各個分店。注意:DVDCopy表以BranchID字段和Branch表關聯,以DVDID字段和DVD表關聯。

下面是部分數據示例:

Branch表:

BIDStreetCity
B001street1New York
B002street2Houston
B003street3LA
B004street4Lincoln

DVD表:

DVDIDCategoryTitle
D001science fictionTransformers IV
D002science fictionTransformers II
D003science fictionGuardians of the Galaxy
D004actThe Expendables III
D005sportNeed for Speed
D006featureGrace of Monaco

DVDCopy表:

CopyIDDVDIDBIDStatusLastDateRentedLastDateReturnedMemberID
C000D001B001
7/10/20147/13/2014M001
C001D004B001
7/10/20147/13/2014M001
C002D001B001
7/10/2014
M001
C003D005B001
7/10/20147/13/2014M003
C004D006B001
7/10/20147/13/2014M003
C005D005B002
7/10/20147/13/2014M003
C006D002B002
7/10/20147/13/2014M006
C007D002B002
7/10/20147/13/2014M007
C008D001B002
7/10/20147/13/2014M008
C009D004B002
7/10/20147/13/2014M009
C010D005B002
7/10/20147/13/2014M010
C011D006B002Miss7/10/20147/13/2014M010
C000D001B003
7/10/20147/13/2014M001
C001D004B003
7/10/20147/13/2014M001
C002D001B003Miss7/10/2014
M001
C003D005B003
7/10/20147/13/2014M003

說明

1.     計算結果應當是Branch表中的某些記錄。

2.     DVDCopy表中的Status字段如果是“Miss”,則說明光盤丟失。LastDateReturned字段如果爲空,則說明光盤借出尚未歸還。顯然,丟失或未歸還的光盤不在計算範圍內,應當過濾掉。

3.     應當考慮某些分店可能在DVDCopy表中不存在記錄,雖然這種情況比較罕見。

解題思路

1.     從DVDCopy表過濾出店裏現存的DVD拷貝(沒有丟失或借出)。

2.     按照BID對DVDCopy表分組,每組就是一個門店所有的DVD拷貝。

3.     找到每個門店的DVD拷貝對應的DVD,再計算出這些DVD的分類數量。

4.     查詢出現存的DVD分類數量小於4的門店,這樣的門店符合要求。

5.     找到DVDCopy表中沒出現過的門店,這樣的門店也符合要求。

6.     將兩類符合要求的門店合併。

SPL代碼


A
1=Branch=db.query("select * from   Branch")
2=DVD=db.query("select * from   DVD")
3=DVDCopy=db.query("select * from   DVDCopy")
4=DVDCopy.switch(DVDID,DVD:DVDID;   BID,Branch:BID)
5=DVDCopy.select(STATUS!="Miss"   && LASTDATERETURNED!=null)
6=A5.group(BID)
7=A6.new(~.BID:BonList,   ~.(DVDID).id(CATEGORY).count():CatCount)
8=A7.select(CatCount<4)
9=A8.(BonList) | (Branch \ A7.(BonList))
10>file("shortage.xlsx").xlsexport@t(A9)

A1-A3:從數據庫中檢索數據,分別命名爲變量Branch、DVD、DVDCopy。計算結果如下:

1554695454575100.png

1554695454656100.png

1554695453221100.png

A4:=DVDCopy.switch(DVDID,DVD:DVDID; BID,Branch:BID)

使用函數switch,將DVDCopy表中的DVDID字段切換成DVD表中對應的記錄,將BID字段切換成Branch表中對應的記錄。這一步是對象式關聯訪問的基礎,計算後DVDCopy的結果如下:

1554695453350100.png

淺藍色字體表示該字段對應爲某條記錄,點擊後可查看,如下圖:

1554695453512100.png

此時,只需用操作符“.”就可以進行對象式關聯訪問,比如DVDCopy.(DVDID). (CATEGORY)表示每個DVD拷貝對應的DVD分類。DVDCopy.(BID)則可以取得每個DVD拷貝對應的分店詳情(完整記錄)。

A5:=DVDCopy.select(STATUS!="Miss" && LASTDATERETURNED!=null)

這句代碼用來過濾數據,即:丟失的,未歸還的DVD拷貝不在計算範圍內,過濾後A5的值如下:

1554695453678100.png

A6:=A5.group(BID)

上述代碼用來對A5中的數據按照BID分組,每行代表一個門店的所有DVD拷貝,如下:

1554695453830100.png

點擊淺藍色字體,可以看到組內成員:

1554695453932100.png

可以看到,函數group只對數據進行分組,並不會同時進行彙總計算,這一點和SQL中的分組函數不同。當我們需要對分組後的數據進行較深入加工,而不是簡單彙總時,用SPL的group函數會更方便,比如A7中的代碼。

A7:=A6.new(~.BID:BonList, ~.(DVDID).id(CATEGORY).count():CatCount)

上述代碼用來計算每個門店對應的DVD拷貝各有幾類。函數new可以根據A6中的數據生成新的對象A7,A7有兩個列:BonList和CatCount,BonList直接來自A6中組內數據的BID列,CatCount來自於組內數據的DVDID列。CatCount的算法分爲三部分:~.(DVDID)找到每個門店所有的DVD拷貝對應的DVD記錄;id(CATEGORY)去除這些DVD記錄中重複的Category;count()用來計算Category的數量。計算結果如下:

1554695454100100.png

即:B002門店有3類DVD拷貝,B003門店有3類,B001門店有4類。

A8:A7.select(CatCount<4)

上述代碼執行查詢,求出CatCount小於4的門店,結果如下:

1554695454158100.png

上述缺貨的門店是根據DVDCopy表計算出的。但有些嚴重缺貨的門店也許不會出現在DVDCopy表,比如該門店所有的DVD拷貝都借出去了,或者該門店完全沒有DVD拷貝,因此要把這部分門店合並進來,代碼如下:

A9:=A8.(BonList) | (Branch \ A7.(BonList))

上述代碼中,運算符“|”表示將兩個數據集進行並集計算(可用union函數代替),運算符“\”表示差集計算(可用函數diff代替)。A8.(BonList)、Branch、A7.(BonList)分別代表:DVDCopy表中缺貨的門店、所有的門店、DVDCopy表中出現過的門店,其值分別爲:

 1554695454258100.png

A9就是本案例最終的計算結果,其值爲:

1554695454371100.png

A10:>file("shortage.xlsx").xlsexport@t(A9)

最後將結果導出到excel文件shortage.xlsx,打開文件查看結果如下:

1554695454460100.png

通過這個例子我們可以看到,SQL缺乏顯式集合,不能用A8或Branch這樣的變量來代表數據集,因此上述簡短的SPL代碼必須用幾個冗長的SQL才能實現。

另外,SPL可被報表工具或java程序調用,調用的方法也和普通數據庫相似,使用它提供的JDBC接口即可向java主程序返回ResultSet形式的計算結果,具體方法可參考相關文檔。【Java如何調用SPL腳本】


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章