【性能优化】7.3 [归并与连接] 关联定位
7.3 关联定位
前两节讨论的都是全表遍历。现实的任务中常常会还会关联表做条件过滤,当然我们可以在关联之后再做过滤,但这样会将所有关联表都遍历一次,表很大时就会耗时很多。有时条件能被迅速执行,过滤后剩下的结果集也可能很小。而两个表之间是主键关联的,用被过滤后的表的主键去查找关联表的记录,可以避免全表遍历,获得更好的性能。
这个算法逻辑有点像是小事实表和大维表的外键关联。但可以利用两个表的主键都有序的特点,就能针对较大的过滤结果集来实施了。
A |
B |
|
1 |
=file("A.ctx").open() |
=file("B.ctx").open() |
2 |
=A1.cursor(;…) |
|
3 |
for A2,1000 |
=B1.find(A3.(id)) |
4 |
=join(A3,id;B3,id) |
|
5 |
… |
B3 中用 A3 的主键去取值,A3 取出来的记录的主键是有序的,这样从 B1 中取出来的记录也不会重复,而且总是从前向后扫描 B 表,最坏情况也就是被遍历一次。不会发生我们在讨论大事实表大维表时如果采用这种办法可能造成的重复多次遍历问题。
不过,这段代码只是个示例说明算法过程,实际上不能简单使用 find 函数,它每次都会从头去找,不会继承上次查找的结果(下次查找基于上次的位置点继续向后找就可以了)。这种利用关联来定位的算法实现并不简单,代码较复杂,其本身开销也不小,有可能抵消掉读取量变小的优势,具体使用场景还要测试。
SPL 封装了这些运算。我们用主子表来举例。
A |
|
1 |
=file("orders.ctx").open().cursor(dt;area=="CA") |
2 |
=file("details.ctx").open().news(A1,dt,price,quantity) |
3 |
=A2.groups(dt;sum(price*quantity)) |
主表被过滤后,再使用过滤后的主表去取出关联的子表记录,因为主表对子表是一对多的关系,所以这里要使用 news,表示主表中取出的每条记录可能对应多条子表记录,此时会把主表字段按关联的子表记录数量复制,相当于做 joinx。
也可以先过滤子表,再去取出关联的主表记录。
A |
|
1 |
=file("details.ctx").open().cursor(price,quantity;quanity>10) |
2 |
=file("orders.ctx").open().new(A1,dt,sum(price*quantity):amount) |
3 |
=A2.groups(dt;sum(amount)) |
返回游标缺省是按后一个关联表为基准的(如果没有任何过滤条件,返回游标的记录数和被关联表的记录数相同),这里也就是主表,子表记录就要先做一次聚合。
这些函数都可以应用到多路游标上。
针对列式计算,SPL 还提供了 pjoin 函数用来实现有序归并以及关联定位。不过优化原理是类似的,这里也就不再详细解释了,有兴趣的读者可以参考 SPL 论坛上的资料。