列列表中 select 的连接方法

join method of select in column list

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

我有这个简单的select:

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 作为驱动 table 的散列连接吗?排序合并?有什么完全不同的吗? 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 的信息。您在 Predicate Information 中看到第 1 filter("C"."ID"=:B1).

换句话说,您将完整扫描 table 以查找 table 中 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 次。

警告 - 执行计划可能会根据 table 统计信息、优化器设置或 Oracle 版本(例如 Oracle 可以 rewrite 子查询并使用连接)。

这只是 11.2 上带有虚拟 tables 的示例。 但是您应该了解如何观察 Oracle 的行为并决定是否需要额外的索引。