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表:

BID Street City
B001 street1 New York
B002 street2 Houston
B003 street3 LA
B004 street4 Lincoln

DVD表:

DVDID Category Title
D001 science fiction Transformers IV
D002 science fiction Transformers II
D003 science fiction Guardians of the Galaxy
D004 act The Expendables III
D005 sport Need for Speed
D006 feature Grace of Monaco

DVDCopy表:

CopyID DVDID BID Status LastDateRented LastDateReturned MemberID
C000 D001 B001
7/10/2014 7/13/2014 M001
C001 D004 B001
7/10/2014 7/13/2014 M001
C002 D001 B001
7/10/2014
M001
C003 D005 B001
7/10/2014 7/13/2014 M003
C004 D006 B001
7/10/2014 7/13/2014 M003
C005 D005 B002
7/10/2014 7/13/2014 M003
C006 D002 B002
7/10/2014 7/13/2014 M006
C007 D002 B002
7/10/2014 7/13/2014 M007
C008 D001 B002
7/10/2014 7/13/2014 M008
C009 D004 B002
7/10/2014 7/13/2014 M009
C010 D005 B002
7/10/2014 7/13/2014 M010
C011 D006 B002 Miss 7/10/2014 7/13/2014 M010
C000 D001 B003
7/10/2014 7/13/2014 M001
C001 D004 B003
7/10/2014 7/13/2014 M001
C002 D001 B003 Miss 7/10/2014
M001
C003 D005 B003
7/10/2014 7/13/2014 M003

说明

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脚本】


猜你喜欢

转载自blog.51cto.com/12749034/2416932