概述
当查询语句中存在半连接(semi join)时,由于半连接不满足交换律,因此只能按照语句定义的顺序进行连接,使得优化器无法根据参与连接的表的实际大小制定最优的方案。为此,Oceanbase中定义了半连接转内连接规则,能够将满足条件的半连接转为内连接,为优化器进一步优化提供了基础。
基本原理
考虑如下情况:
SELECT * FROM t1 SEMI JOIN t2 on t1.c1 = t2.c1
我们可以将上述查询直接转为内连接,如下所示:
SELECT t1.* FROM t1, t2 WHERE t1.c1 = t2.c1
这里可能存在一个问题,即当连接条件的右表部分(如t2.c1)不唯一时,在内连接执行时可能会造成左表记录被复制多份(即一条左表记录对应多条右表记录),从而违背原来的语义。因此仅当上述右表满足唯一条件时,才可以进行改写。
代码解析
半连接转内连接规则的入口为ObTransformSemiToInner::transform_one_stmt,该函数最终调用transform_semi_to_inner函数执行改写。由于这项改写并不一定能够带来更好的执行开销,因此需要在改写完成后调用accept_transform函数判断是否接受改写结果。
transform_semi_to_inner函数首先会调用check_basic_validity函数判断查询语句是否满足改写条件,如果满足,则调用do_transform函数执行改写。
check_basic_validity函数负责判断指定的半连接是否可以被改写为内连接,能够被转换的半连接需要满足如下条件:
- 半连接的连接条件均为equal表达式。
- 满足以下两个条件中的一个:半连接条件的右表表达式对应唯一结果或可以通过视图查询转换为唯一结果;半连接位于某个exist/no_exist/any/all子查询中,此时由于转内连接造成的数据重复可以被忽略。
该函数首先调用check_semi_join_condition函数提取半连接条件的左右表达式集合,并判断是否所有条件都是equal条件,然后按照如下流程判断是否满足改写条件:
- 调用check_right_table_output_one_row函数判断右表是否为视图查询且添加了limit 1表达式,此时右表至多输出一行记录。
- 调用check_right_exprs_unique函数判断右表表达式是否对应唯一结果。
- 调用check_stmt_is_non_sens_dul_vals函数判断半连接语句是否位于某个exist/no_exist/any/all子查询中。
- 调用check_can_add_distinct函数判断是否可以通过将右表转化为视图表,然后在视图查询中添加distinct的方式保证结果唯一。如果可以,则调用check_join_condition_match_index函数进一步检查左表条件是否与某个索引匹配(这里应该是性能上的考量)。
do_transform函数负责执行实际的改写操作,该函数执行逻辑较为简单。如果右表满足上述1,2,3项中的任意一项,则将右表添加到查询语句中的from部分即可(相当于隐式的inner join);如果满足第4项,则将右表转换为视图查询,然后将查询语句设置为distinct。