【问题标题】:Why it has HASH JOIN in this execution plan (explain plan)?为什么它在这个执行计划(解释计划)中有HASH JOIN?
【发布时间】:2019-08-20 09:47:23
【问题描述】:

我正在测试一些 Oracle 语句及其执行计划,但遇到了这个问题(内部加入 2 个表):

SELECT COUNT(*) 
FROM WF_TRANSITION T, 
     WF_VERSION_REQUEST_TYPE VRT 
WHERE T.FK_VS_REQUEST_TYPE_ID = VRT.VS_REQUEST_TYPE_ID + 0

这是它的执行计划:

我的问题是为什么我们有第 6 步 HASH JOIN,同时我们之前做了 NESTED LOOP。我认为这个嵌套循环加入了 2 个表 WF_TRANSITIONWF_VERSION_REQUEST_TYPE,不需要 HASH JOIN。 谁能给我解释一下?

【问题讨论】:

    标签: oracle sql-execution-plan


    【解决方案1】:

    您有一个适应性计划。数据库将根据处理的行数选择执行hash joinnested loop

    您可以通过statistics collector 步骤来判断这一点。这是计算在wf_version_reqeuest_types_pk 上从扫描中流出的行数。

    如果此数字低于阈值,它将使用nested loop。在此之上,它将切换到hash join

    要找出它做了什么,请为查询获取execution plan。如果您在使用DBMS_XPlan 时添加+ADAPTIVE 选项,它会通过在这些操作前加上- 来显示哪个连接被丢弃:

    set serveroutput off
    
    select /*+ gather_plan_statistics */*
    from   hr.employees e
    join   hr.departments d
    on     e.department_id = d.department_id;
    
    select * 
    from   table(dbms_xplan.display_cursor(null, null, 'ROWSTATS LAST +ADAPTIVE'));
    
    Plan hash value: 4179021502                                                                 
    
    ----------------------------------------------------------------------------------------    
    |   Id  | Operation                     | Name              | Starts | E-Rows | A-Rows |    
    ----------------------------------------------------------------------------------------    
    |     0 | SELECT STATEMENT              |                   |      1 |        |    106 |    
    |  *  1 |  HASH JOIN                    |                   |      1 |    106 |    106 |    
    |-    2 |   NESTED LOOPS                |                   |      1 |    106 |     27 |    
    |-    3 |    NESTED LOOPS               |                   |      1 |        |     27 |    
    |-    4 |     STATISTICS COLLECTOR      |                   |      1 |        |     27 |    
    |     5 |      TABLE ACCESS FULL        | DEPARTMENTS       |      1 |     27 |     27 |    
    |- *  6 |     INDEX RANGE SCAN          | EMP_DEPARTMENT_IX |      0 |        |      0 |    
    |-    7 |    TABLE ACCESS BY INDEX ROWID| EMPLOYEES         |      0 |      4 |      0 |    
    |     8 |   TABLE ACCESS FULL           | EMPLOYEES         |      1 |    107 |    107 |    
    ----------------------------------------------------------------------------------------    
    
    Predicate Information (identified by operation id):                                         
    ---------------------------------------------------                                         
    
       1 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")                                      
       6 - access("E"."DEPARTMENT_ID"="D"."DEPARTMENT_ID")                                      
    
    Note                                                                                        
    -----                                                                                       
       - this is an adaptive plan (rows marked '-' are inactive) 
    

    【讨论】:

    • 你的答案很好。谢谢你的解释。我可以再问你一个小问题吗?我使用提示强制嵌套循环加入:SELECT /*+ USE_NL(T VRT) */ COUNT(*) FROM WF_TRANSITION T, WF_VERSION_REQUEST_TYPE VRT WHERE T.FK_VS_REQUEST_TYPE_ID = VRT.VS_REQUEST_TYPE_ID + 0。为什么它的执行计划在两个表上都使用 INDEX FULL SCAN:link。我认为它应该在 NESTED LOOP 连接上使用 WF_TRANSITION 上的索引。附加信息:WF_TRANSITION 有 785 行,非唯一,WF_VERSION_REQUEST_TYPE 有 90 行,唯一主键。对我来说真的很奇怪。
    • VRT.VS_REQUEST_TYPE_ID + 0 应用函数;这可以防止index range 扫描。删除+ 0
    • 我故意添加VRT.VS_REQUEST_TYPE_ID + 0 以使用VRT 表(有90 行)作为NESTED LOOP 的驱动表,优化器必须在WF_TRANSITION T 表的索引上使用index range scans(其中有 785 行)。但是,优化器在T 表上使用fast full scan。你能解释一下吗?真的很奇怪。
    • 像这样试图智取优化器通常是个坏主意。如果它的统计数据准确,它通常会有很好的计划。如果删除 + 0 没有给出“正确”的计划,请提交一个新问题,包括你得到的 execution plan。使用dbms_xplan 就像我在上面所做的那样。格式使用ALLSTATS LAST;确保计划包括A-rowsE-rowsBuffers 列。您可以省略自适应选项。
    猜你喜欢
    • 1970-01-01
    • 2014-01-07
    • 2012-05-21
    • 1970-01-01
    • 1970-01-01
    • 2011-03-10
    • 2012-12-26
    • 2017-03-11
    • 1970-01-01
    相关资源
    最近更新 更多