【程序设计】10.3 [找关联] 归并

 

10.3 归并

假如有两个 Excel 文件,比如前面说的订单表,可能由两个人分别制作,其中就可能会有些重复的记录,我们需要把这些重复记录挑出来,然后合并成一个没有重复记录的表。

本来,这是个很简单地集合求并的任务,我们学过集合运算,可以直接使用交交差等运算符就完事。但是,我们还知道序表中的不同记录永远不会相等,不同序表的记录更不会相等,从 Excel 文件读出来的序表当然是不同序表,这时候就不能适合集合运算了。

SPL 提供了归并运算函数来完成这个任务:


A

B

1

=T("data1.xlsx")

=T("data2.xlsx")

2

=[A1,B1].merge@uo()

>T("data.xlsx",A2)

很简单,merge 函数将对比记录的内容。其中的 @u 就表示求并集,如果没有这个选项,它会把两个序表直接拼起来,也就是求和集(即 |)。@o 等下再说,先写上就行了。

类似地,merge 函数还可以求交集,即寻找出重复的记录,把 @u 换成 @i 就行了。


A

B

1

=T("data1.xlsx")

=T("data2.xlsx")

2

=[A1,B1].merge@io()

>T("data.xlsx",A2)

merge 函数会对比整条记录的所有字段,如果有任何一个字段不相同,就会认为两条记录是不同的。这样的比较会比较费时,而且有时候可能也没有必要。比如这个订单问题,我们认为制作人是认真负责的,只要订单号正确了,订单内容就不会有错,但订单收集过程中可能有重复和遗漏,导致两份文件中有重复的订单号,这样只要将订单号重复的记录挑出来即可。


A

B

1

=T("data1.xlsx")

=T("data2.xlsx")

2

=[A1,B1].merge@uo(ID)

>T("data.xlsx",A2)

在 merge 函数中加个参数,就可以要求它只对比这些字段。

或者对于设置了主键的记录,merge 函数就会只对比主键了。


A

B

1

=T("data1.xlsx").keys(ID)

=T("data2.xlsx").keys(ID)

2

=[A1,B1].merge@uo()

>T("data.xlsx",A2)

对于交集也是一样,把 @u 换成 @i 就可以。

情况再复杂一些,两个制作人都可能出错。我们要看看两个文件中订单号相同的记录是不是内容有出入, 这就要把订单号相同记录挑出来再做全内容对比看看是不是相同。


A

B

1

=T("data1.xlsx")

=T("data2.xlsx")

2

=[A1,B1].merge@io(ID)

=[B1,A1].merge@io(ID)

3

=[A2,B2].merge@do()

=A3.(ID)

A2 将计算出 A1 中订单号和 B1 有重复的那些记录,但保留的是由 A1 的记录构成的排列。对应的,B2 将保留 B1 中与 A1 的订单号重复的记录。A2 和 B2 都是 A1 和 B1 的交集,但只是在比较订单号意义上的交集,其全内容还不一定相同。然后在 A3 用 merge@d 做 A2 对 B2 的全内容差集,这样就可以挑出 A2 中内容与 B2 对应订单不同的记录,在 B3 中取出这些订单的订单号,也就是要继续审查的订单了。

当集合运算涉及到结构化数据时, 会出现这种复杂的情况。主键(或某个较关键的字段)相同,不表示整条记录相同。求交并差要判断记录相同时,有可能只使用主键,也可能要使用整条记录。如果只使用主键,会发生不满足交换律的现象,这并不只成员次序不同,而可能是内容完全不同。在写代码时要特别注意。

现在再来说这个 @o 选项。merge 函数缺省情况会假定数据有序(比如都是从小到大),这样比较的速度非常快,不要再做排序。@o 选项则告诉 merge 函数现在数据是乱序的,需要先排序。我们目前这个例子中,订单都是按订单号有序排列的,不写 @o 也没关系。

还有个注意点,merge 函数前面是个序列([A1,B1]),它可以同时计算出多个排列的并、交结果。不过相对不常见,这里不再举例了。

merge 函数对序列也有效,不过单值序列一般直接用集合运算符了,很少用 merge 函数。

【程序设计】 前言及目录

【程序设计】10.2 [找关联] 外键

【程序设计】10.4 [找关联] 连接