在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")