UNION ALL的Oracle Optimizer问题

时间:2014-03-11 23:23:35

标签: sql oracle union-all optimization

我对Oracle优化器的这种行为感到非常困惑。这与联合CTE的所有操作有关。如果有人有任何想法我都是游戏。

 --Relevant data structures:
 --t_positionperf (index: POSITIONPERF_X1 (account_id, hist_date, security_id)
 --t_positionhist (index: POSITIONHIST_X1 (position_id, hist_date, hist_type)
 --                       PK_T_POSITIONHIST (hist_date, position_id, hist_type)
 --v_positiontype (very simple "case-when" translation of a tiny lookup table)

with q as (
select 
pp.position_id, pp.hist_date, pp.account_id, pp.income, pp.expense,
ph.position_type_id, ph.price, ph.quantity, ph.factor, ph.daily_accrual,
n.daily_accrual as new_daily_accrual, nvl(n.is_loan, v.is_loan) as new_is_loan
from
t_positionperf pp
left outer join t_positionhist ph
            on  pp.position_id = ph.position_id
            and pp.hist_date = ph.hist_date
            and ph.hist_type = 'O' --the 'O' join set from t_positionhist
     left outer join v_positiontype v 
                 on  ph.position_type_id = v.position_type_id
     left outer join (
                      select 
                      x.position_id, x.hist_date, x.daily_accrual, v2.is_loan 
                      from t_positionhist x 
                      join v_positiontype v2 on x.position_type_id = v2.position_type_id
                      where x.hist_type = 'N'
                     ) n --the 'N' join set from t_positionhist
                on  ph.position_id = n.position_id 
                and ph.hist_date = n.hist_date
where pp.account_id in (5018,5312)
and pp.hist_date between to_date('01-jan-14') and  to_date('31-jan-14')
)
select 
q.account_id,q.hist_date,q.position_id,q.income,
q.expense,q.position_type_id,q.price,q.quantity,q.factor
from q

这使用合理的访问路径。

Statistics
----------------------------------------------------------
         29  recursive calls
          0  db block gets
      37640  consistent gets
       5115  physical reads
          0  redo size
     227442  bytes sent via SQL*Net to client
       4952  bytes received via SQL*Net from client
        409  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       6116  rows processed


Execution Plan:
--------------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name              | Rows  | Bytes | Cost (%CPU)  | Time     |
--------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |                   |   941 | 88454 |    5283   (1)| 00:01:04 |
|*  1 |  FILTER                          |                   |       |       |              |          |
|   2 |   NESTED LOOPS OUTER             |                   |   941 | 88454 |    5283   (1)| 00:01:04 |
|*  3 |    HASH JOIN RIGHT OUTER         |                   |   941 | 71516 |    2456   (1)| 00:00:30 |
|   4 |     TABLE ACCESS FULL            | T_POSITIONTYPE    |    64 |  1216 |       3   (0)| 00:00:01 |
|   5 |     NESTED LOOPS OUTER           |                   |   941 | 53637 |    2452   (1)| 00:00:30 |
|   6 |      INLIST ITERATOR             |                   |       |       |              |          |
|   7 |       TABLE ACCESS BY INDEX ROWID| T_POSITIONPERF    |   941 | 22584 |     567   (1)| 00:00:07 |
|*  8 |        INDEX RANGE SCAN          | POSITIONPERF_X1   |   941 |       |       6   (0)| 00:00:01 |
|   9 |      TABLE ACCESS BY INDEX ROWID | T_POSITIONHIST    |     1 |    33 |       2   (0)| 00:00:01 |
|* 10 |       INDEX UNIQUE SCAN          | POSITIONHIST_X1   |     1 |       |       1   (0)| 00:00:01 |
|  11 |    VIEW PUSHED PREDICATE         |                   |     1 |    18 |       3   (0)| 00:00:01 |
|  12 |     NESTED LOOPS                 |                   |     1 |    27 |       4   (0)| 00:00:01 |
|  13 |      TABLE ACCESS BY INDEX ROWID | T_POSITIONHIST    |     1 |    20 |       3   (0)| 00:00:01 |
|* 14 |       INDEX UNIQUE SCAN          | POSITIONHIST_X1   |     1 |       |       2   (0)| 00:00:01 |
|  15 |      TABLE ACCESS BY INDEX ROWID | T_POSITIONTYPE    |     1 |     7 |       1   (0)| 00:00:01 |
|* 16 |       INDEX UNIQUE SCAN          | PK_T_POSITIONTYPE |     1 |       |       0   (0)| 00:00:01 |
--------------------------------------------------------------------------------------------------------

但是,如果从CTE中为此选择添加UNION ALL,那么查询将如下所示:

with q as (...) 
select q.account_id, q... etc.
UNION ALL
select q.account_id, max(q...) etc.
group by q.account_id

优化器现在想要实现CTE(TEMP TABLE TRANSFORMATION)。这部分对我来说很有意义,因为来自同一CTE的UNION ALL。我了解的原因是为什么实现CTE会导致它选择不同的(并且更低劣的)访问路径。在构建N' N'时,它似乎失去了将谓词推送到T_POSITIONHIST表的能力。加入集。您可以看到它现在在T_POSITIONHIST上执行全表扫描,因此磁盘I / O已经通过屋顶

Statistics
----------------------------------------------------------
        362  recursive calls
         53  db block gets
     546116  consistent gets
     521527  physical reads
        688  redo size
     250541  bytes sent via SQL*Net to client
       4952  bytes received via SQL*Net from client
        409  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       6117  rows processed


Execution Plan
------------------------------------------------------------------------------------------------------------------
| Id  | Operation                          | Name                        | Rows  | Bytes | Cost (%CPU)| Time     |
------------------------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                   |                             |  1882 |   205K|    13  (54)| 00:00:01 |
|   1 |  TEMP TABLE TRANSFORMATION         |                             |       |       |            |          |
|   2 |   LOAD AS SELECT                   | SYS_TEMP_0FD9D6CB3_54C7EBF8 |       |       |            |          |
|*  3 |    FILTER                          |                             |       |       |            |          |
|*  4 |     HASH JOIN OUTER                |                             |   941 | 81867 |   147K  (3)| 00:29:32 |
|*  5 |      HASH JOIN RIGHT OUTER         |                             |   941 | 63047 |  2456   (1)| 00:00:30 |
|   6 |       VIEW                         | V_POSITIONTYPE              |    64 |   448 |     3   (0)| 00:00:01 |
|   7 |        TABLE ACCESS FULL           | T_POSITIONTYPE              |    64 |   448 |     3   (0)| 00:00:01 |
|   8 |       NESTED LOOPS OUTER           |                             |   941 | 56460 |  2452   (1)| 00:00:30 |
|   9 |        INLIST ITERATOR             |                             |       |       |            |          |
|  10 |         TABLE ACCESS BY INDEX ROWID| T_POSITIONPERF              |   941 | 22584 |   567   (1)| 00:00:07 |
|* 11 |          INDEX RANGE SCAN          | POSITIONPERF_X1             |   941 |       |     6   (0)| 00:00:01 |
|  12 |        TABLE ACCESS BY INDEX ROWID | T_POSITIONHIST              |     1 |    36 |     2   (0)| 00:00:01 |
|* 13 |         INDEX UNIQUE SCAN          | POSITIONHIST_X1             |     1 |       |     1   (0)| 00:00:01 |
|  14 |      VIEW                          |                             |  2358K|    44M|   145K  (3)| 00:29:02 |
|  15 |       VIEW                         |                             |  2358K|    51M|   145K  (3)| 00:29:02 |
|* 16 |        HASH JOIN                   |                             |  2358K|    67M|   145K  (3)| 00:29:02 |
|  17 |         VIEW                       | V_POSITIONTYPE              |    64 |   448 |     3   (0)| 00:00:01 |
|  18 |          TABLE ACCESS FULL         | T_POSITIONTYPE              |    64 |   448 |     3   (0)| 00:00:01 |
|* 19 |         TABLE ACCESS FULL          | T_POSITIONHIST              |  2358K|    51M|   145K  (3)| 00:29:01 |
|  20 |   UNION-ALL                        |                             |       |       |            |          |
|  21 |    VIEW                            |                             |   941 |   106K|     6   (0)| 00:00:01 |
|  22 |     TABLE ACCESS FULL              | SYS_TEMP_0FD9D6CB3_54C7EBF8 |   941 | 56460 |     6   (0)| 00:00:01 |
|  23 |    HASH GROUP BY                   |                             |   941 |    99K|     7  (15)| 00:00:01 |
|  24 |     VIEW                           |                             |   941 |    99K|     6   (0)| 00:00:01 |
|  25 |      TABLE ACCESS FULL             | SYS_TEMP_0FD9D6CB3_54C7EBF8 |   941 | 56460 |     6   (0)| 00:00:01 |
------------------------------------------------------------------------------------------------------------------

奇怪的是,您可以通过复制 CTE来部分解决这个问题,如下所示:

with q as (...), q2 as (.../*duplicate of q*/)
select q.account_id, q... etc.
UNION ALL
select q2account_id, max(q2...) etc.
group by q2.account_id

因为它不再想要实现结果集,所以它能够使用更智能的访问路径,除非它必须执行两次,因此成本大约翻了一番(但仍然远远低于真正的糟糕程度)它上面使用的访问路径)。

统计

     57  recursive calls
      0  db block gets
  73855  consistent gets
      0  physical reads
      0  redo size
 202937  bytes sent via SQL*Net to client
   4953  bytes received via SQL*Net from client
    409  SQL*Net roundtrips to/from client
      0  sorts (memory)
      0  sorts (disk)
   6117  rows processed

1 个答案:

答案 0 :(得分:0)

所以最终我发现了答案:显然,优化者决定实现CTE('使用'块)会破坏优化器执行基于成本的能力查询转换反过来又破坏了执行某些操作的能力,例如Join Predicate Pushdown(在上面的解释计划中的 View Pushed Predicate )和Complex View Merge(一种re方法) - 写查询块)。这可能会导致优化器对大型表执行不必要的全表扫描等可怕的事情,当您期望它使用附加到查询的谓词的某些索引访问时。

我在其他几种情况下遇到了这个问题,最后在做研究时遇到了以下有启发性的博文: http://oracle-randolf.blogspot.com/2008/01/subquery-factoring-and-cost-based-query.html

看来最初CTE的任何使用都禁用了CBQT,但是在11.2.0.3中,如果WITH视图被内联(即未实现),优化器将使用CBQT,所以现在问题只有在选择WITH视图实现时才可见。请参阅链接中的评论: Timur Akhmadeev 说... 只是想注意11.2.0.3中不再存在该问题。它被跟踪在#34;如果所有WITH视图都被内联,则允许CBQT" (11740670)

这里看到的两种策略都是这样解释的:(1)重复WITH视图和(2)Yaroslav Shabalin建议使用INLINE提示