【性能优化】7.4 [归并与连接] 附表

 

【性能优化】7.3 [归并与连接] 关联定位

7.4 附表

某个事实表可能随意和多个维表建立外键关联,甚至和同一个维表建立多个外键关联,但基于双方主键建立的同维关联和主子关联就没有这么随意。

同维关联是一种等价关系(A 与 B 同维则 B 与 A 同维,A 与 B 同维且 B 与 C 同维则 A 也与 C 同维),可以用同维关系把所有数据表分成若干组,同一组内互相同维关联,而不会和组外的表有同维关联。也就是说,如果要进行同维关联,一定在这些表之间发生。这些同组的同维表中,通常有一个是最大的,它的主键值是完整的,其它同维表的主键值都是它的子集。比如客户表中会有全部的客户主键值,VIP 客户表中只有部分客户主键值但会有 VIP 客户的更多属性。

主子关联略复杂一些,但也会有一定的固定性,合理的数据结构设计中,子表只会和唯一的主表建立主子关联,比如订单明细表的主表只有订单表,不会再以另一个表为主表。从子表上看到的主表是唯一的。

基于这种认识,我们可以把同组的同维表和主子表绑在一起存储。

对于同一组的同维表,先找出主键值完整的那个表,称为基表,其它同维表称为附表。基表建立好之后,附表的字段就作为基表记录的附加字段存储,也可以认为是基表的字段,只是这些字段对于很多记录是没有值的。

主子表关系时,以主表为基表,子表作为附表。子表的字段也作为主表记录的附加字段。不同的是,这些附加字段的取值是个集合,而且来自同一个子表的附加字段,其取值集合的长度是相同的(作为同一个主表记录的附加字段时)。同样的,这些附加字段也可能没有值。

由于同维关联和主子关联的相对固定性,绑定存储不会影响其关联关系,也不会影响和其它表的外键关联。

这样做在性能方面的好处如下几点:

1) 基表和附表有共同的主键,把附表字段作为基表记录的附加字段存储时,只要有一套主键存储就可以了,不需要再多存储附表(与基表关联的)主键了,存储量会更小。采用列存方式时,关联时要能读取的数据量也变少了。

2) 附表字段可以作为基表记录的附加字段直接被引用(来自子表的字段是个集合,引用方式会不同),不需要再做去关联比对了,计算量会变小。特别地,如果基表被过滤了,附表会自然会过滤,不需要再使用上节说的关联定位方法。反过来也成立。

3) 附表字段作为基表记录的字段,和基表记录绑在一起,分段时天然同步的,不需要再做特殊的跟随分段。

不过,这种存储方式也有坏处,因为存储方案变得更复杂了,在引用附加字段时会有不少额外的判断。

通常在主键及关联较为复杂时,比如主键字段有多个,或主子关联的 1:N 比例中 N 更大(意味着常规关联时会有更多的对比),使用附表方案就会有更大的优势。单一简单主键的同维表关联时,优势不太明显,甚至可能会有劣势。

理论上,主子关联关系时的附表还可以再有附表,但不太常见了。

我们下面用主子表为举例。

SPL 在组表上实现了附表功能,需要在创建组表指定附加字段。


A

1

=file("orders.ctx").open().cursor()

2

=file("details.ctx").open().cursor()

3

=file("order_detail.ctx").create(#ID,…)

4

=A3.attach(detail,#seq,…)

5

=A3.append@i(A1)

6

=A4.append@i(A2)

A3 创建一个常规的组表,A4 在 A3 附加一个附表,会有一个名字及附表的字段,对于子表还要设计自己的主键(和主表共同的主键不需要再写了),然后和普通数据表一样追加数据,SPL 会根据使用子表的主键把记录附加到正确的主表记录上。需要注意,基表和附表除了主键外,不能有同名的字段,否则会发生混淆。

正因为组表是基表和附表的组合,才称为组表。我们把组表中的基表和附表称为实表

创建好有附表的组表后,就可以在运算中引用附表的字段了。


A

1

=file("order_detail.ctx").open()

2

=A1.cursor(dt,detail.sum(quantity):quantity)

3

=A2.groups(dt;sum(quantity))

quantity 是附表 detail 的字段,因为是子表,所以取值是集合,从主表引用时需要加上聚合运算。

也可以再还原出子表的记录:


A

1

=file("order_detail.ctx").open()

2

=A1.cursor(dt,detail{price,quantity}:amount)

3

=A2.run(amount=amount.sum(price*quantity))

4

=A3.groups(dt;sum(amount))

因为子表记录是多个,还原之后将作为主表游标的一个字段,其取值是一个序表。生成序表等动作比较复杂,这样做会损失性能,有可能抵消减少关联带来的优势。

还可以从附表来引用基表字段:


A

1

=file("order_detail.ctx").open().attch(detail)

2

=A1.cursor(dt,price,quantity)

3

=A2.groups(dt;sum(price*quantity))

引用基表字段时直接写就可以了,性能会比上面那种生成序列字段的方法更好。

这些运算都可以支持多路游标。

需要提起注意的是,我们在讲这些关联算法时,常常会说不一定总能做到性能更优。关联是个较复杂的运算,其优化算法的实现代码也很复杂。虽然从理论分析上看,这些算法大都能降低运算复杂度,但实际写出来的代码很复杂时,工程上的影响也不能被忽略。

这些算法都经过实际测试的验证,在某些场景下确实能起到优化性能的作用,但并不是所有场景。具体使用哪种算法,要在熟悉这些算法后再因地制宜地选择。

【性能优化】8.1 [多维分析] 部分预汇总

【性能优化】 前言及目录