【问题标题】:Oracle Connect by and Hash joinOracle Connect by 和 Hash join
【发布时间】:2016-04-10 15:21:54
【问题描述】:

我有一个问题 1:

select *
from (
select 'null' parentcode, u.unititemcode childcode, null unititempartnbr, null partoid,
null proccode, null unititemtype, null qualcat, null unititemgm2, null rsncode
from wi_unititem u where
u.prodproccode='PM11' and
u.prodendtime>sysdate-10
union all
select p.unititemcode parentcode, u.unititemcode childcode, 0 unititempartnbr, p.oid partoid,
p.proccode, u.unititemtype, u.qualcat, u.unititemgm2, u.rsncode
from wi_uitempart p, wi_unititem u where
p.unititemparttype='USEDSET' and
u.prodproccode=p.proccode and
u.setid=p.setid    
) m
where m.unititemtype<>'SKID'
start with m.parentcode='null'
connect by prior m.childcode=m.parentcode

解释查询 1 的计划:

SELECT STATEMENT  ALL_ROWSCost: 64  Bytes: 2 745  Cardinality: 9                                    
23 FILTER                               
    22 CONNECT BY WITH FILTERING                            
        9 VIEW TIPSMSY. Cost: 12  Bytes: 610  Cardinality: 2                        
            8 UNION-ALL                     
                2 TABLE ACCESS BY INDEX ROWID TABLE TIPSMSY.WI_UNITITEM Cost: 4  Bytes: 29  Cardinality: 1                  
                    1 INDEX RANGE SCAN INDEX TIPSMSY.WI_UNITITEM_PROCCODE_ENDTIME Cost: 3  Cardinality: 1           
                7 TABLE ACCESS BY INDEX ROWID TABLE TIPSMSY.WI_UNITITEM Cost: 3  Bytes: 46  Cardinality: 1                  
                    6 NESTED LOOPS  Cost: 8  Bytes: 113  Cardinality: 1             
                        4 TABLE ACCESS BY INDEX ROWID TABLE TIPSMSY.WI_UITEMPART Cost: 5  Bytes: 67  Cardinality: 1         
                            3 INDEX RANGE SCAN INDEX (UNIQUE) TIPSMSY.WI_UITEMPART_PK Cost: 4  Cardinality: 1   
                        5 INDEX RANGE SCAN INDEX TIPSMSY.WI_UNITITEM_SETID Cost: 2  Cardinality: 1          
        21 VIEW TIPSMSY. Cost: 64  Bytes: 2 745  Cardinality: 9                         
            20 UNION-ALL                    
                12 FILTER               
                    11 TABLE ACCESS BY INDEX ROWID TABLE TIPSMSY.WI_UNITITEM Cost: 4  Bytes: 29  Cardinality: 1             
                        10 INDEX RANGE SCAN INDEX TIPSMSY.WI_UNITITEM_PROCCODE_ENDTIME Cost: 3  Cardinality: 1          
                19 TABLE ACCESS BY INDEX ROWID TABLE TIPSMSY.WI_UNITITEM Cost: 3  Bytes: 46  Cardinality: 1                 
                    18 NESTED LOOPS  Cost: 60  Bytes: 904  Cardinality: 8           
                        16 NESTED LOOPS         
                            13 CONNECT BY PUMP      
                            15 TABLE ACCESS BY INDEX ROWID TABLE TIPSMSY.WI_UITEMPART Cost: 18  Bytes: 938  Cardinality: 14     
                                14 INDEX RANGE SCAN INDEX (UNIQUE) TIPSMSY.WI_UITEMPART_PK Cost: 4  Cardinality: 43  
                        17 INDEX RANGE SCAN INDEX TIPSMSY.WI_UNITITEM_SETID Cost: 2  Cardinality: 1         

查询2:

select *
from (
select 'null' parentcode, u.unititemcode childcode, null unititempartnbr, null partoid,
null proccode, null unititemtype, null qualcat, null unititemgm2, null rsncode
from wi_unititem u where
u.prodproccode='PM11' and
u.prodendtime>sysdate-20
union all
select p.unititemcode parentcode, u.unititemcode childcode, 0 unititempartnbr, p.oid partoid,
p.proccode, u.unititemtype, u.qualcat, u.unititemgm2, u.rsncode
from wi_uitempart p, wi_unititem u where
p.unititemparttype='USEDSET' and
u.prodproccode=p.proccode and
u.setid=p.setid    
) m
where m.unititemtype<>'SKID'
start with m.parentcode='null'
connect by prior m.childcode=m.parentcode

解释查询 2 的计划:

SELECT STATEMENT  ALL_ROWSCost: 122  Bytes: 82 655  Cardinality: 271                                    
23 FILTER                               
    22 CONNECT BY WITH FILTERING                            
        9 VIEW TIPSMSY. Cost: 70  Bytes: 80 520  Cardinality: 264                       
            8 UNION-ALL                     
                2 TABLE ACCESS BY INDEX ROWID TABLE TIPSMSY.WI_UNITITEM Cost: 62  Bytes: 7 627  Cardinality: 263                
                    1 INDEX RANGE SCAN INDEX TIPSMSY.WI_UNITITEM_PROCCODE_ENDTIME Cost: 4  Cardinality: 263             
                7 TABLE ACCESS BY INDEX ROWID TABLE TIPSMSY.WI_UNITITEM Cost: 3  Bytes: 46  Cardinality: 1                  
                    6 NESTED LOOPS  Cost: 8  Bytes: 113  Cardinality: 1             
                        4 TABLE ACCESS BY INDEX ROWID TABLE TIPSMSY.WI_UITEMPART Cost: 5  Bytes: 67  Cardinality: 1         
                            3 INDEX RANGE SCAN INDEX (UNIQUE) TIPSMSY.WI_UITEMPART_PK Cost: 4  Cardinality: 1   
                        5 INDEX RANGE SCAN INDEX TIPSMSY.WI_UNITITEM_SETID Cost: 2  Cardinality: 1          
        21 VIEW TIPSMSY. Cost: 122  Bytes: 82 655  Cardinality: 271                         
            20 UNION-ALL                    
                12 FILTER               
                    11 TABLE ACCESS BY INDEX ROWID TABLE TIPSMSY.WI_UNITITEM Cost: 62  Bytes: 7 627  Cardinality: 263           
                        10 INDEX RANGE SCAN INDEX TIPSMSY.WI_UNITITEM_PROCCODE_ENDTIME Cost: 4  Cardinality: 263        
                19 TABLE ACCESS BY INDEX ROWID TABLE TIPSMSY.WI_UNITITEM Cost: 3  Bytes: 46  Cardinality: 1                 
                    18 NESTED LOOPS  Cost: 60  Bytes: 904  Cardinality: 8           
                        16 HASH JOIN        
                            13 CONNECT BY PUMP      
                            15 TABLE ACCESS BY INDEX ROWID TABLE TIPSMSY.WI_UITEMPART Cost: 18  Bytes: 938  Cardinality: 14     
                                14 INDEX FULL SCAN INDEX (UNIQUE) TIPSMSY.WI_UITEMPART_PK Cost: 4  Cardinality: 43  
                        17 INDEX RANGE SCAN INDEX TIPSMSY.WI_UNITITEM_SETID Cost: 2  Cardinality: 1         

查询 1 包含“sysdate-10”,查询 2 包含“sysdate-20”,这是唯一的区别,查询 1 运行速度很快,而查询 2 运行速度很慢。

如果我们比较执行计划,那么可以在从 14 和 16 开始的行中发现差异: 查询 1 使用嵌套循环连接 + 索引范围扫描 查询2使用Hash Join + Index全扫描

查询2也可以使用查询1的执行计划吗?

【问题讨论】:

  • 查询 2 正在处理更多数据。是什么让你认为查询 1 的计划是查询 2 增加数据的正确计划?
  • 查询 2 返回的行数大约是查询 1 的 2 倍。查询 1 计划是正确的,因为发生索引全扫描的表 WI_UITEMPART 包含数百万个此类索引。查询 1 只产生大约 10000 行,这意味着查询 2 产生大约 20000 行。
  • 两个查询的第一个选择有一个列null unititemtype,然后是where m.unititemtype&lt;&gt;'SKID'。因为NULL 永远不会等于或不等于NULL,所以第一个查询中的任何行都不会包含在最终结果中,除非以connect by 开头的内容带回了where 子句消除的内容。另外,您是否尝试过将查询参数化为sysdate - :bind_variable?用 10 运行一次,然后 20,然后 20 应该重用计划。一个实验,而不是一个解决方案。
  • 关于NULL:你是对的,但它不影响执行速度。正如你所说,我已经尝试使用绑定变量 - 计划与查询 2 相同(错误计划)

标签: oracle oracle10g nested-loops hierarchical


【解决方案1】:

复制执行计划的最简单方法是使用全套大纲提示。

使用这两个语句生成带有大纲数据的解释计划。这也是生成解释计划的最佳方式,原因在我的回答 here 中进行了描述。

explain plan for select * from dual; --Add your real query here.
select * from table(dbms_xplan.display(format => '+outline'));

Plan hash value: 272002086

--------------------------------------------------------------------------
| Id  | Operation         | Name | Rows  | Bytes | Cost (%CPU)| Time     |
--------------------------------------------------------------------------
|   0 | SELECT STATEMENT  |      |     1 |     2 |     2   (0)| 00:00:01 |
|   1 |  TABLE ACCESS FULL| DUAL |     1 |     2 |     2   (0)| 00:00:01 |
--------------------------------------------------------------------------

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

  /*+
      BEGIN_OUTLINE_DATA
      FULL(@"SEL$1" "DUAL"@"SEL$1")
      OUTLINE_LEAF(@"SEL$1")
      ALL_ROWS
      DB_VERSION('12.1.0.2')
      OPTIMIZER_FEATURES_ENABLE('12.1.0.2')
      IGNORE_OPTIM_EMBEDDED_HINTS
      END_OUTLINE_DATA
  */

使用大纲数据作为提示,以确保执行计划保持不变。尽管“保证”可能是一个强词;这些未记录的提示可能在不同版本中或查询在语义上不同时不起作用。

select 
  /*+
      BEGIN_OUTLINE_DATA
      FULL(@"SEL$1" "DUAL"@"SEL$1")
      OUTLINE_LEAF(@"SEL$1")
      ALL_ROWS
      DB_VERSION('12.1.0.2')
      OPTIMIZER_FEATURES_ENABLE('12.1.0.2')
      IGNORE_OPTIM_EMBEDDED_HINTS
      END_OUTLINE_DATA
  */
* from dual;

这应该可行,但有点难看。理想情况下,您希望找到 Oracle 做出错误决定的根本原因 - 调查估计与实际基数、统计数据等。但是有很多方法可以做到这一点,而且需要很长时间。作为一种折衷方案,您可能希望尝试使用提示并尝试将其范围缩小到解决性能问题所必需的一两个。

【讨论】:

  • 嗨乔恩,非常感谢您的回答。我已经尝试过您的执行计划显示方式 - 它显示的信息与我的执行计划相同。您能否提供一些建议如何找到问题的根本原因,或者建议使用哪些提示(我尝试了 USE_NL,但它被忽略了)?
  • 您是否将这两个语句作为常规 SQL 运行?不要通过某些 IDE 的解释计划“工具”来运行它们。
  • 也许可以试试这个提示:select /*+ index(p) */ p.unititemcode parentcode ....。如果没有完整的表和索引定义,就很难重现计划。
  • 提示 /*+ index(p) */ 不起作用。我不能在这里发布完整的表和索引定义,因为表非常大(超过 100 列)并且包含 10-30 个索引。错误计划的原因显然是返回的行数增加了,但是这些数量远少于表的行数。我开始认为这种行为是某种oracle“内部连接”行为,因为递归返回的结果(查看计划中的“CONNECT BY PUMP”)由哈希连接连接。我没有发现影响这种行为的提示,是否有任何提示可能影响分层查询?
  • 我只是尝试使用大纲数据提示,但没有成功。查询 2 的计划没有改变。
猜你喜欢
  • 1970-01-01
  • 2016-05-22
  • 1970-01-01
  • 2013-09-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-12-10
  • 1970-01-01
相关资源
最近更新 更多