这是一个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_A
和TAB_B
的哈希联接的结果联接?是否为每个哈希联接结果在嵌套循环中访问一次?它是将TAB_C
作为驱动表的哈希联接吗?排序合并吗?完全不同吗?此计划后面的TAB_C
可以有不同的加入方法,还是总是相同的?
非常感谢您!
答案 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的行为以及确定是否需要其他索引有印象。