这是一个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
可以在这个计划背后有不同的连接方法,还是总是相同的?
非常感谢你!
首先,您应该使用更新的技术来检查执行计划,例如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的行为并决定是否需要其他索引。