Oracle 的优化器是不是考虑子查询的基础列?

Posted

技术标签:

【中文标题】Oracle 的优化器是不是考虑子查询的基础列?【英文标题】:Does Oracle's Optimizer Take into Consideration a Subquery's Underlying Columns?Oracle 的优化器是否考虑子查询的基础列? 【发布时间】:2019-01-05 03:26:07 【问题描述】:

假设您有这样的查询:

with subselect as (
   select foo_id 
     from foo
) 
select bar_id 
  from bar
  join subselect on foo_id = bar_id
 where foo_id = 1000

假设您在 foo_id 上有一个索引。 Oracle 的数据库是否足够智能,可以在查询中使用“where foo_id = 1000”行的索引? OR 由于 foo_id 被包裹在子查询中,Oracle 是否会丢失与该列相关的索引信息?

【问题讨论】:

计划说明了什么? 查询优化器会考虑查询中的所有列。是什么让你觉得不是?您可以通过检查查询计划来验证这一点。 有点不可能肯定地回答(也许优化器会进行表扫描,因为最大的表中只有 5 行,使用索引不值得努力)但没有理由假设,因为您使用优化器太笨而无法考虑它们的子查询。 优化器分析查询的每一位以确定一个计划,在较新的版本中,如果它发现它做出了一个不太理想的决定,它甚至会在执行过程中更改计划。子查询并不特殊......它们基本上被视为任何其他“常规”查询。使用索引并不总是让事情变得更好。查看执行计划以了解如何处理您的子查询 【参考方案1】:

执行一个简单的测试:

create table foo as
select t.object_id as foo_id, t.* from all_objects t;

create table bar as
select t.object_id as bar_id, t.* from all_objects t;

create index foo_id_ix on foo(foo_id);

exec dbms_stats.GATHER_TABLE_STATS(ownname=>user, tabname=>'FOO', method_opt=>'FOR ALL INDEXED COLUMNS' );

explain plan for 
with subselect as (
   select foo_id 
     from foo
) 
select bar_id 
  from bar
  join subselect on foo_id = bar_id
 where foo_id = 1000;

 select * from table( DBMS_XPLAN.DISPLAY );

最后一次查询的结果是:

Plan hash value: 445248211

----------------------------------------------------------------------------------
| Id  | Operation            | Name      | Rows  | Bytes | Cost (%CPU)| Time     |
----------------------------------------------------------------------------------
|   0 | SELECT STATEMENT     |           |     1 |    10 |   366   (1)| 00:00:01 |
|   1 |  MERGE JOIN CARTESIAN|           |     1 |    10 |   366   (1)| 00:00:01 |
|*  2 |   TABLE ACCESS FULL  | BAR       |     1 |     5 |   365   (1)| 00:00:01 |
|   3 |   BUFFER SORT        |           |     1 |     5 |     1   (0)| 00:00:01 |
|*  4 |    INDEX RANGE SCAN  | FOO_ID_IX |     1 |     5 |     1   (0)| 00:00:01 |
----------------------------------------------------------------------------------

Predicate Information (identified by operation id):
---------------------------------------------------

   2 - filter("BAR_ID"=1000)
   4 - access("FOO_ID"=1000)

在上面的示例中,Oracle 使用|* 4 | INDEX RANGE SCAN 使用索引:FOO_ID_IX 过滤4 - access("FOO_ID"=1000) 所以答案是:是的,Oracle 的数据库足够智能,可以使用查询中的索引来查找“where foo_id = 1000”这一行

【讨论】:

以上是关于Oracle 的优化器是不是考虑子查询的基础列?的主要内容,如果未能解决你的问题,请参考以下文章

oracle怎样查询数据库函数是不是被执行

mysql中,如何向测试人员介绍连接查询和子查询的优劣势?

SQL调优指南笔记4:Query Optimizer Concepts

sql调优方法实用性总结

[转] SQL性能调优日常积累

oracle查询优化