这是一个 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 行中,您可以看到starts = 100,这意味着FULL SCAN
启动了100 次。
警告 - 执行计划可能会根据表统计信息、优化器设置或 Oracle 版本而变化(例如,Oracle 可以重写子查询并使用联接)。
这只是 11.2 上虚拟表的示例。 但您应该了解如何观察 Oracle 的行为并决定是否需要额外的索引。
关于oracle - 列列表中select的join方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55235946/