【程序设计】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 函数。