为什么Oracle在一个查询中对同一个表同时执行TABLE SCAN和INDEX UNIQUE SCAN?

nzrxty8p  于 2023-04-11  发布在  Oracle
关注(0)|答案(1)|浏览(176)

我不是在寻找以下优化,只是一个解释.我有这个查询:

SELECT COUNT(LARGE_A.id_a), SUM(LARGE_A.b_integer)
FROM LARGE_A
INNER JOIN MEDIUM_A ON LARGE_A.id_a = MEDIUM_A.id
INNER JOIN MEDIUM_B ON LARGE_A.id_b = MEDIUM_B.id
WHERE
    MEDIUM_A.a_varchar2 LIKE 'Example%' AND
    EXTRACT(YEAR FROM MEDIUM_B.a_datetime) = 2000 AND
    LARGE_A.a_integer BETWEEN 0 AND 1000;

属性的类型写在它们的名称中。ID是整数。表LARGE_A有1 000 000行,中等的有100 000行。ID是主键,但是表LARGE_A有一个复合键(id_a, id_b)
然后我从V$SQL视图中得到了以下执行计划,因为Autotrace在这一点上撒谎了。

SELECT STATEMENT                        
|SORT AGGREGATE                         
||HASH JOIN                             
|||NESTED LOOPS                         
||||NESTED LOOPS                        
|||||STATISTICS COLLECTOR               
||||||NESTED LOOPS                      
|||||||TABLE ACCESS FULL                LARGE_A
|||||||TABLE ACCESS BY INDEX ROWID      MEDIUM_A
||||||||INDEX UNIQUE SCAN               PK(MEDIUM_A)
|||||INDEX UNIQUE SCAN                  PK(MEDIUM_B)
||||TABLE ACCESS BY INDEX ROWID         MEDIUM_B
|||TABLE ACCESS FULL                    MEDIUM_B

PK表示括号中给出的表的主键(整数)上的非聚集索引。这里我不使用IOT。
为什么优化器首先从索引PK(MEDIUM_B)中逐个查询记录,然后使用ROWID获取其余的行,然后再次运行FULL表扫描?

yqlxgs2m

yqlxgs2m1#

你看到的是一个完整的计划,而不是真正的计划。(在v$sql中,full_plan_hash_valueplan_hash_value之间的差异)。自12 c起,Oracle有自适应计划,如果它在运行时发现它的基数猜测偏离很远,它就能够从一个计划更改到另一个计划。完整的计划提供了两个选项-散列连接+表B的全扫描,或表B的嵌套循环+索引访问。这完全取决于它从表A获得多少行,以确定哪一个是最佳计划。
你可以这样看得最清楚:

EXPLAIN PLAN FOR ....
/
SELECT * FROM TABLE(dbms_xplan.display(format=>'ADAPTIVE'))
/

你会在禁用的计划行旁边看到-标记,默认情况下不会使用...但它们仍然是计划的一部分,因此如果Oracle决定在执行中切换,它可以启用这些行并禁用原始默认值。要查看代码在运行时实际使用的内容,你必须通过获取v$session.sql_id / sql_child_number来观察它,然后执行:

SELECT * FROM TABLE(dbms_xplan.display_cursor([sqlid],[childnumber]))

/

相关问题