优化oracle查询

时间:2010-04-01 16:55:37

标签: oracle optimization

我很难绕过这个问题。它需要几乎200多秒才能执行。我也粘贴了执行计划。

SELECT       
      user_id    ,
      ROLE_ID              ,
      effective_from_date  ,
      effective_to_date    ,
      participant_code     ,
      ACTIVE
FROM    
      CMP_USER_ROLE E
WHERE   
      ACTIVE = 0
 AND  (SYSDATE BETWEEN effective_from_date AND effective_to_date 
        OR TO_CHAR(effective_to_date,'YYYY-Q') = '2010-2')
 AND  participant_code = 'NY005'
 AND  NOT EXISTS
            ( SELECT 1 FROM CMP_USER_ROLE r 
              WHERE r.USER_ID= E.USER_ID 
              AND r.role_id = E.role_id 
              AND r.ACTIVE = 4
              AND E.effective_to_date 
                      <= ( SELECT MAX(last_update_date)
                           FROM CMP_USER_ROLE S
                           WHERE S.role_id = r.role_id
                           AND S.role_id = r.role_id
                           AND S.ACTIVE = 4 ))

解释计划

-----------------------------------------------------------------------------------------------------
| Id  | Operation                        | Name             | Rows  | Bytes | Cost (%CPU)| Time     |
-----------------------------------------------------------------------------------------------------
|   0 | SELECT STATEMENT                 |                  |     1 |    37 |   154   (2)| 00:00:02 |
|*  1 |  FILTER                          |                  |       |       |            |          |
|*  2 |   TABLE ACCESS BY INDEX ROWID    | USER_ROLE        |     1 |    37 |    30   (0)| 00:00:01 |
|*  3 |    INDEX RANGE SCAN              | N_USER_ROLE_IDX6 |    27 |       |     3   (0)| 00:00:01 |
|*  4 |   FILTER                         |                  |       |       |            |          |
|   5 |    HASH GROUP BY                 |                  |     1 |    47 |   124   (2)| 00:00:02 |
|*  6 |     TABLE ACCESS BY INDEX ROWID  | USER_ROLE        |   159 |  3339 |   119   (1)| 00:00:02 |
|   7 |      NESTED LOOPS                |                  |    11 |   517 |   123   (1)| 00:00:02 |
|*  8 |       TABLE ACCESS BY INDEX ROWID| USER_ROLE        |     1 |    26 |     4   (0)| 00:00:01 |
|*  9 |        INDEX RANGE SCAN          | N_USER_ROLE_IDX5 |     1 |       |     3   (0)| 00:00:01 |
|* 10 |       INDEX RANGE SCAN           | N_USER_ROLE_IDX2 |   957 |       |    74   (2)| 00:00:01 |
-----------------------------------------------------------------------------------------------------

统计:

Statistics
----------------------------------------------------------
          0  recursive calls
          0  db block gets
    3433602  consistent gets
          0  physical reads
          0  redo size
      58149  bytes sent via SQL*Net to client
       1260  bytes received via SQL*Net from client
        148  SQL*Net roundtrips to/from client
          0  sorts (memory)
          0  sorts (disk)
       2199  rows processed

3 个答案:

答案 0 :(得分:3)

嗯,我受到了deja vu的攻击。<​​/ p>

无论如何,这是你需要做的事情:

3433602  consistent gets

三百万个逻辑IO会耗费大量时间,因此您需要做的就是减少这个数量。

您的查询包含针对同一个表的三组访问。每次访问都包含一个索引读取,后跟一个表读取。从your comment to Peter's question开始,您的统计信息似乎相当准确(查询返回699415行,NUM_ROWS = 697608)。

调整是一项复杂的活动,需要考虑很多因素。我们可以轻松地花半天时间浏览这一个查询可能出错的所有内容。

例如,您是否收集索引和表的统计信息(在早期版本的Oracle中,默认情况下不收集索引统计信息)?如果你有统计数据那些索引的聚类因子是什么?您的所有索引访问都是扫描,因此聚类因子是相关的。

我们想要的是一个低聚类因子,因为这意味着索引必须做更少的工作才能从表中获取行。如果聚类因子更接近索引中的条目数是坏的,而如果它更接近表中的块数是好的。唉,考虑到你所经历的LIO数量,我的资金来自不良的聚类因素。所以你需要从索引中获得更多的果汁。

查看查询,最外层投影中的列将用于查询的WHERE子句和/或子查询中。尽管如此,您使用的是三个不同的索引,但它们都没有提供满足条件所需的所有信息(因此附加表读取和后续过滤)。在这些情况下可以非常有效的一种策略是构建一个包含所有必要列的超级索引。

create index N_USER_ROLE_IDX23 on user_role    
       ( active
         , role_id
         , user_id
         , participant_code
         , effective_from_date
         , effective_to_date
         , last_update_date )

这导致ACTIVE和ROLE_ID,因为这些列用于所有三组标准。 (顺便提一句,你的第三个问题是:

                       WHERE S.role_id = r.role_id
                       AND S.role_id = r.role_id

这是对的吗? )无论如何,这个索引的要点是它满足所有三个WHERE子句和最终投影,因此它根本不需要触摸表格。因此,它可以大大减少一致获取的数量。

答案 1 :(得分:0)

首先要分析你的表格:

EXEC dbms_stats.gather_table_stats('YOUR_SCHEMA', 'CMP_USER_ROLE');

您是否仍然获得相同的执行计划?

执行计划中的Time列看起来好像您的查询对于Oracle优化器而言看起来并不昂贵。

答案 2 :(得分:0)

尝试在查询中运行EXPLAIN PLAN,看看它是否正在进行表扫描。

我猜这个条款会有问题:

OR TO_CHAR(effective_to_date,'YYYY-Q') = '2010-2')

我认为调用WHERE子句中的函数会强制Oracle扫描每一行,因为它必须在每一列上评估该函数,以便查看该行是否是结果集的一部分。您可以通过这种方式使索引无效。

更好的解决方案是对索引列值进行搜索,该值不需要函数调用来评估它。我推荐像“DATE_COLUMN BETWEEN x和y”这样的东西,其中x和y是该季度的开始和结束日期。确保DATE_COLUMN上有索引。