【性能优化】6.3 [外键关联] 外键序号化
6.3 外键序号化
从事实表的外键关联维表,本质上是一个查找动作。在事实表较大时无法做到预关联了,如果有办法能提高查找速度那也可以获得好的关联性能。
维表上通常已经建立有哈希索引,回顾一下第一章讲过的内存查找技术,比哈希查找更快的算法,就只有序号定位了。
如果维表的主键值本身就是序号,那可以直接使用。但大部分情况并不是,需要把事实表的外键值转换成序号,也就是外键序号化。
A |
|
1 |
=file("product.btx").import@b().keys@i(pid) |
2 |
=file("orders.btx").cursor@b() |
3 |
=A2.run(p_id=A1.pfind(p_id)) |
4 |
>file("orders_new.btx").export@b(A3) |
用 pfind 可以找出查找值在序表中的序号,用来替换原表的外键值后,就可以使用更快速的序号定位实现外存的关联了。
A |
|
1 |
=file("product.btx").import@b() |
2 |
=file("orders_new.btx").cursor@b(p_id,quantity) |
3 |
=A2.switch(p_id,A1:#) |
4 |
=A3.groups(p_id.vendor;sum(p_id.price*quantity)) |
#参数可以让 switch 函数使用序号定位,这时候维表的索引也没必要生成(可以节省一些内存)。后续的运算代码不需要改变。
外键序号化的关联性能和地址化几乎是一样的,这样对于外存数据表也可以获得和内存预关联一样的高性能,仅剩的差别只是事实表太大而要从外存读取。
不过,它需要事先做准备,改变事实表的外键值。而且转换结果依赖于维表中的记录次序,如果维表发生插入删除等变化,就要重新生成一次事实表,维护管理成本相对较高。
序号化也可以用在 join 函数中以适应多字段外键和要保留外键值的场景,这里不再举例了。
有些维表的主键并不是序号,但可以简单处理一下和某种序号关联起来。类似我们在第一章讲过的序号定位查找,有时需要用 align 把维表整理成按序号排序的。
比如上例中,pid 并不是连续的序号,但我们知道它总是在 1-1000 之内,那也可以先把维表记录处理成按序号排序的排列:
A |
|
1 |
=file("product.btx").import@b().align(1000,pid) |
2 |
=file("orders.btx").cursor@b(p_id,quantity) |
3 |
=A2.switch(p_id,A1:#) |
4 |
=A3.groups(p_id.vendor;sum(p_id.price*quantity)) |
这种情况就不必重新生成事实表,可以直接用原表。
还有一种情况,维表主键本身不是序号,但通过简单运算可以得到序号。
比如 pid 是个字符串,第一个字符是个大写字母,后两位都是数字。这样我们可以用
(asc(left(pid,1))-asc("A"))*100+int(right(pid,2))+1
把 pid 转换成一个 1-2700 之间的自然数,也就和序号关联起来了。那么这时候是不是也可以避免重新生成事实表呢?
这要具体看这个转换计算的复杂度。如果是个简单的加减法则问题不大,但如果像上面这种表达式才能转换成序号,那还是需要重新生成事实表的,否则每次查找维表记录时都要对事实表外键字段做这样的运算,这会非常浪费时间,造成的结果很可能还不如哈希索引。为了提高性能,需要事先把对事实表外键做好这样的计算。
A |
|
1 |
=file("orders.btx").cursor@b() |
2 |
=A1.run(p_id=(asc(left(p_id,1))-asc("A"))*100+int(right(p_id,2))+1) |
3 |
>file("orders_new.btx").export@b(A2) |
这种转换比之前的办法略有一点优势,只基于事实表数据就可以完成,不依赖于维表中的记录次序。维表只要保持这种规则,在有增删后不必重新生成新的事实表。
A |
|
1 |
=file("product.btx").import@b() |
2 |
=A1.align(2700,(asc(left(pid,1))-asc("A"))*100+int(right(pid,2))+1) |
3 |
=file("orders_new.btx").cursor@b(p_id,quantity) |
4 |
=A3.switch(p_id,A2:#) |
5 |
=A4.groups(p_id.vendor;sum(p_id.price*quantity)) |
维表主键可以不必转换,每次加载后的数据准备阶段来做就可以了。
我们在第一章还讲过多层序号定位的技术。有些维表的主键直接与序号关联时无法实用,但使用多层序号就有可能(比如前述的身份证号,直接转换成序号会导致占用空间太大,而如果数据分布合适时可以转换成多层序号)。这时候也可以对维表建立多层序号索引再用外键来关联(再复习一下:外键关联本质上是查找,可以使用各种索引)。但同样的,因为将主键转换成多层序号的运算通常并不会太简单,所以也要先把外键转换好才能获得好的关联性能,还是需要重新生成事实表。比完全序号化的好处也仅在于刚才说的,这个转换不依赖于维表记录的次序,维表有增删时不必重新转换。
多层序号是个序列,存储和读取都不太方便。在 SPL 中可以使用排号键,将多层序号作为单值处理。