列列表中选择的联接方法

时间:2019-03-19 07:55:20

标签: oracle oracle11g sql-execution-plan

这是一个11g的问题,但我想这与版本无关。

我有一个简单的选择:

create table tab_a (id number);
create table tab_b (id number);
create table tab_c (id number);

select 
    a.id,b.id,(select c.id from tab_c c where c.id = a.id) 
from 
    tab_a a join tab_b b on a.id = b.id;

哪个给我这样的计划:

SELECT LPAD(' ',depth)||OPERATION||'_'||OPTIONS||' '||OBJECT_NAME plan
FROM v$sql_plan
WHERE plan_hash_value = 2530031923
order by id;
SELECT STATEMENT_ 
 TABLE ACCESS_FULL TAB_C
 HASH JOIN_ 
  TABLE ACCESS_FULL TAB_A
  TABLE ACCESS_FULL TAB_B

现在我的问题是:TAB_C如何与TAB_ATAB_B的哈希联接的结果联接?是否为每个哈希联接结果在嵌套循环中访问一次?它是将TAB_C作为驱动表的哈希联接吗?排序合并吗?完全不同吗?此计划后面的TAB_C可以有不同的加入方法,还是总是相同的?

非常感谢您!

1 个答案:

答案 0 :(得分:3)

首先,您应该使用较新的技术来检查执行计划,例如DBMS_XPLAN.DISPLAY`

EXPLAIN PLAN  SET STATEMENT_ID = 'sqlx' into   plan_table  FOR
select 
    a.id,b.id,(select c.id from tab_c c where c.id = a.id) 
from 
    tab_a a join tab_b b on a.id = b.id;


SELECT * FROM table(DBMS_XPLAN.DISPLAY('plan_table', 'sqlx','ALL'));    

    ----------------------------------------------------------------------------
| Id  | Operation          | Name  | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |   100 |  2600 |     7  (15)| 00:00:01 |
|*  1 |  TABLE ACCESS FULL | TAB_C |     1 |    13 |     3   (0)| 00:00:01 |
|*  2 |  HASH JOIN         |       |   100 |  2600 |     7  (15)| 00:00:01 |
|   3 |   TABLE ACCESS FULL| TAB_A |   100 |  1300 |     3   (0)| 00:00:01 |
|   4 |   TABLE ACCESS FULL| TAB_B |   100 |  1300 |     3   (0)| 00:00:01 |
----------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("C"."ID"=:B1)
   2 - access("A"."ID"="B"."ID")

这为您提供了如何访问TAB_C的信息。您会在谓词信息中看到第1行filter("C"."ID"=:B1)

换句话说,您将对表A和B之间的每个ID 进行全扫描,这当然是不希望的。

如果您不相信这种简单的方法,请运行查询并收集计划统计信息

select /*+ gather_plan_statistics */
    a.id,b.id,(select c.id from tab_c c where c.id = a.id) 
from 
    tab_a a join tab_b b on a.id = b.id;  

---
select * from table(dbms_xplan.display_cursor(null,null,'ALLSTATS LAST')); 

SQL_ID  4m4a1cp4gyjkv, child number 0
-------------------------------------
select /*+ gather_plan_statistics */     a.id,b.id,(select c.id from 
tab_c c where c.id = a.id)  from      tab_a a join tab_b b on a.id = 
b.id

Plan hash value: 2606630813

-----------------------------------------------------------------------------------------------------------------
| Id  | Operation          | Name  | Starts | E-Rows | A-Rows |   A-Time   | Buffers |  OMem |  1Mem | Used-Mem |
-----------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT   |       |      1 |        |    100 |00:00:00.01 |      15 |       |       |          |
|*  1 |  TABLE ACCESS FULL | TAB_C |    100 |      1 |    100 |00:00:00.01 |     700 |       |       |          |
|*  2 |  HASH JOIN         |       |      1 |    100 |    100 |00:00:00.01 |      15 |  1517K|  1517K| 1256K (0)|
|   3 |   TABLE ACCESS FULL| TAB_A |      1 |    100 |    100 |00:00:00.01 |       7 |       |       |          |
|   4 |   TABLE ACCESS FULL| TAB_B |      1 |    100 |    100 |00:00:00.01 |       8 |       |       |          |
-----------------------------------------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   1 - filter("C"."ID"=:B1)
   2 - access("A"."ID"="B"."ID")

在第1行中,您看到开始= 100,这意味着FULL SCAN启动了100次。

警告-执行计划可能会根据表统计信息,优化器设置或Oracle版本而更改(例如,Oracle可以重写子查询并使用联接)。

这只是在11.2上具有虚拟表的示例。 但是您应该对如何观察Oracle的行为以及确定是否需要其他索引有印象。