如何创建一个查询,产生这个解释计划? (oracle sql)

时间:2014-12-02 11:34:07

标签: oracle oracle-sqldeveloper sql-execution-plan

数据库是示例Oracle HR数据库:http://elsasoft.com/samples/oracle/Oracle.XE.HR/default.htm

解释计划:

----------------------------------------------------------
| Id | Operation                     | Name              |
----------------------------------------------------------
|  0 | SELECT STATEMENT              |                   |
|  1 |  HASH UNIQUE                  |                   |
|* 2 |   TABLE ACCESS BY INDEX ROWID | EMPLOYEES         |
|* 3 |    INDEX RANGE SCAN           | EMP_DEPARTMENT_IX |
----------------------------------------------------------

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

 2 - filter("MANAGER_ID" < 150)
 3 - access("DEPARTMENT_ID" < 50)

我尝试了这个查询,但结果却截然不同:

select /*+ use_hash(emp) */* 
  from HR.employees emp 
 where MANAGER_ID <150 and 
       DEPARTMENT_ID <50;

我已经从filteraccess构建了where语句。来自use_hash的{​​{1}}。但结果仍然非常不同,我不知道如何解决它

2 个答案:

答案 0 :(得分:4)

创建精确的解释计划很困难,取决于查询,版本,参数和未记录的提示。

在这种情况下,主提示可能是未记录的USE_HASH_AGGREGATION,但它也必须与DISTINCTGROUP BY结合使用。但它还取决于使用哪个列 - 如果查询仅在主键上执行了不同的操作,那么它就不会聚合,因为优化器知道没有必要。

由于我使用12c,我必须禁用_optimizer_batch_table_access_by_rowid,但这对于早期版本不是必需的。

未记录的format => '+outline'功能有助于创建精确的计划。如果你不使用12c,很难保证它会以同样的方式工作。 This SQL Fiddle适用于11gR2,但很难知道提示是否有效,或者只是运气,计划是否相同。

<强>查询

explain plan for
select
    /*+
        BEGIN_OUTLINE_DATA
        USE_HASH_AGGREGATION(@"SEL$1")
        INDEX_RS_ASC(@"SEL$1" "EMP"@"SEL$1" ("EMPLOYEES"."DEPARTMENT_ID"))
        OUTLINE_LEAF(@"SEL$1")
        ALL_ROWS
        OPT_PARAM('_optimizer_batch_table_access_by_rowid' 'false')
        DB_VERSION('12.1.0.1')
        OPTIMIZER_FEATURES_ENABLE('12.1.0.1')
        IGNORE_OPTIM_EMBEDDED_HINTS
        END_OUTLINE_DATA
    */
    distinct first_name
from HR.employees emp 
where MANAGER_ID <150 and 
    DEPARTMENT_ID <50;

<强>计划

select * from table(dbms_xplan.display(format => 'basic +predicate +outline'));

Plan hash value: 2074795195

----------------------------------------------------------
| Id  | Operation                    | Name              |
----------------------------------------------------------
|   0 | SELECT STATEMENT             |                   |
|   1 |  HASH UNIQUE                 |                   |
|*  2 |   TABLE ACCESS BY INDEX ROWID| EMPLOYEES         |
|*  3 |    INDEX RANGE SCAN          | EMP_DEPARTMENT_IX |
----------------------------------------------------------

Outline Data
-------------

  /*+
      BEGIN_OUTLINE_DATA
      USE_HASH_AGGREGATION(@"SEL$1")
      INDEX_RS_ASC(@"SEL$1" "EMP"@"SEL$1" ("EMPLOYEES"."DEPARTMENT_ID"))
      OUTLINE_LEAF(@"SEL$1")
      ALL_ROWS
      OPT_PARAM('_optimizer_batch_table_access_by_rowid' 'false')
      DB_VERSION('12.1.0.1')
      OPTIMIZER_FEATURES_ENABLE('12.1.0.1')
      IGNORE_OPTIM_EMBEDDED_HINTS
      END_OUTLINE_DATA
  */

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

   2 - filter("MANAGER_ID"<150)
   3 - access("DEPARTMENT_ID"<50)

答案 1 :(得分:2)

首先尝试在桌面上收集统计数据,如果上面的计划有意义,那么你会得到它

exec dbms_stats.gather_table_stats('HR','EMP', cascade=>true);

如果你还没有得到这个计划,那么oracle认为有一个更好的计划(他通常是对的)。 要强制执行此计划,请尝试

select /*+ USE_INDEX(eMP,EMP_DEPARTMENT_IX ) */ 
from HR.employees emp 
where MANAGER_ID <150 and DEPARTMENT_ID <50