lightdb -- Oracle兼容 -- 支持Oracle多表层级查询的下推优化

在Oracle数据库中,层级查询可以很方便地查询含义等级关系的数据,例如工资表与部门表之间的下属关系。

而在实际应用中,多数情况下这些关系数据分布在多个表中,需要跨表进行层级查询。


对于两种方式连接多表:隐式连接和显示连接。隐式连接指没有明确指定JOIN子句;

显示连接指明确使用JOIN关键字指定连接方式。

这两种连接方式在WHERE子句中的连接条件下推上具有区别:


  • 对于隐式连接,Oracle会试图将WHERE子句中的连接条件下推到实际的连接操作中。

  • 而对于显示连接,由于用户已经明确指定了连接逻辑,Oracle不会再修改用户定义的连接条件下推位置。


下推连接条件的影响很明显:


  • 对于隐式连接,连接条件下推后可以利用索引更快地连接表,通常性能更优。

  • 但是如果条件下推错误,可能导致结果错误。


所以,在实际开发中需要灵活选择:

比如下面的 sql ,隐式的多表关联查询(没有显示的写 join )。

SELECT s1.staff_id, s1.name, s1.manager_id
 FROM staff_table s1, staff_table_2 s2
 WHERE s1.staff_id = s2.staff_id AND s1.staff_id > 1001
 START WITH s1.staff_id = '1001'
 CONNECT BY PRIOR s1.staff_id = s1.manager_id;

Oracle 执行计划如下,可以看到 WHERE 子句中的 s1.staff_id = s2.staff_id 被下推到了连接节点,而 s1.staff_id > 1001 还是保留在原来的位置。

------------------------------------------------------------------
| Id  | Operation                                | Name          |
------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |               |
|*  1 |  FILTER                                  |               |
|*  2 |   CONNECT BY NO FILTERING WITH START-WITH|               |
|   3 |    NESTED LOOPS                          |               |
|   4 |     TABLE ACCESS FULL                    | STAFF_TABLE   |
|*  5 |     INDEX UNIQUE SCAN                    | SYS_C00109736 |
------------------------------------------------------------------
                                                                 
Predicate Information (identified by operation id):              
---------------------------------------------------              
                                                                 
   1 - filter("S1"."STAFF_ID">1001)                              
   2 - access("S1"."MANAGER_ID"=PRIOR "S1"."STAFF_ID")           
       filter("S1"."STAFF_ID"=1001)                              
   5 - access("S1"."STAFF_ID"="S2"."STAFF_ID")

如果显示的指定了 join ,那么 where 条件中的连接条件就不能再进行下推,否则结果错误。

SELECT s1.staff_id, s1.name, s1.manager_id
 FROM staff_table s1 join staff_table_2 s2
 ON s1.manager_id = s2.manager_id
 WHERE s1.staff_id = s2.staff_id AND s1.staff_id > 1001
 START WITH s1.staff_id = '1001'
 CONNECT BY PRIOR s1.staff_id = s1.manager_id;
------------------------------------------------------------------
| Id  | Operation                                | Name          |
------------------------------------------------------------------
|   0 | SELECT STATEMENT                         |               |
|*  1 |  FILTER                                  |               |
|*  2 |   CONNECT BY NO FILTERING WITH START-WITH|               |
|*  3 |    HASH JOIN                             |               |
|   4 |     TABLE ACCESS FULL                    | STAFF_TABLE   |
|   5 |     TABLE ACCESS FULL                    | STAFF_TABLE_2 |
------------------------------------------------------------------
Predicate Information (identified by operation id):                
---------------------------------------------------                                            
   1 - filter("S1"."STAFF_ID"="S2"."STAFF_ID" AND "S1"."STAFF_ID">1001) 
   2 - access("S1"."MANAGER_ID"=PRIOR "S1"."STAFF_ID")                   
       filter("S1"."STAFF_ID"=1001)                                     
   3 - access("S1"."MANAGER_ID"="S2"."MANAGER_ID")


请使用浏览器的分享功能分享到微信等