优化Oracle查询

时间:2010-06-29 02:15:45

标签: sql oracle optimization

对此查询运行解释计划我正在获取全表访问权。

使用的两个表是:

user_role:   803507 rows
cmp_role:    27 rows

查询:

SELECT 
   r.user_id, r.role_id, r.participant_code, MAX(status_id) 
  FROM 
    user_role r, 
    cmp_role c 
  WHERE 
    r.role_id = c.role_id 
    AND r.participant_code IS NOT NULL 
    AND c.group_id = 3 
    GROUP BY 
    r.user_id, r.role_id, r.participant_code 
    HAVING MAX(status_id) IN (SELECT b.status_id FROM USER_ROLE b 
                              WHERE (b.ACTIVE = 1 OR ( b.ACTIVE IN ( 0,3 )  
    AND SYSDATE BETWEEN b.effective_from_date AND b.effective_to_date 
                                    )) 
                             )

如何更好地编写此查询,以便在合适的时间内返回结果。以下是索引:

idx 1 = role_id
idx 2 = last_updt_user_id
idx 3 = actv_id, participant_code, effective_from_Date, effective_to_date
idx 4 = user_id, role_id, effective_from_Date, effective_to_date
idx 5 = participant_code, user_id, roke_id, actv_cd

解释计划:

Q_PLAN
--------------------------------------------------------------------------------
  SELECT STATEMENT
    FILTER
      HASH GROUP BY
        HASH JOIN
          TABLE ACCESS BY INDEX ROWID ROLE
            INDEX RANGE SCAN N_ROLE_IDX2
          TABLE ACCESS FULL USER_ROLE
      TABLE ACCESS BY INDEX ROWID USER_ROLE
        INDEX UNIQUE SCAN U_USER_ROLE_IDX1
    FILTER
      HASH GROUP BY
        HASH JOIN
          TABLE ACCESS BY INDEX ROWID ROLE
            INDEX RANGE SCAN N_ROLE_IDX2
          TABLE ACCESS FULL USER_ROLE
      TABLE ACCESS BY INDEX ROWID USER_ROLE
        INDEX UNIQUE SCAN U_USER_ROLE_IDX1

我没有足够的权力在桌面上运行统计数据

尝试了以下更改但仅削减了1或2秒:

WITH CTE AS (SELECT b.status_id FROM USER_ROLE b 
                                  WHERE (b.ACTIVE = 1 OR ( b.ACTIVE IN ( 0,3 )  
        AND SYSDATE BETWEEN b.effective_from_date AND b.effective_to_date 
                                        )) 
                                 )
    SELECT 
       r.user_id, r.role_id, r.participant_code, MAX(status_id) 
      FROM 
        user_role r, 
        cmp_role c 
      WHERE 
        r.role_id = c.role_id 
        AND r.participant_code IS NOT NULL 
        AND c.group_id = 3 
        GROUP BY 
        r.user_id, r.role_id, r.participant_code 
        HAVING MAX(status_id) IN (select * from CTE)

4 个答案:

答案 0 :(得分:3)

首先你有子查询

SELECT b.status_id FROM USER_ROLE b 
WHERE (b.ACTIVE = 1 
        OR ( b.ACTIVE IN ( 0,3 )  
        AND SYSDATE BETWEEN b.effective_from_date AND b.effective_to_date )
      )

除了全表扫描之外,您无法执行任何操作来获得该结果。 你可能错过了一个联接,但不知道你希望你的查询做什么,我们无法告诉你。

其次,根据group_id为3的cmp_role记录的比例,以及user_role与这些角色匹配的比例,最好在那里进行全面扫描。例如,如果27个cmp_role记录中有3个在第3组中,并且100,000个user_role记录与那些cmp_role记录匹配,那么对表执行单次扫描比执行100,000次索引查找更有效。

答案 1 :(得分:0)

  1. 收集表格的统计信息
  2. 解释查询计划并显示结果。

答案 2 :(得分:0)

我认为以下方法会起作用。我认为子查询只会被评估一次,因为它没有相关性 - 这似乎不是这样的。我在sh演示中对销售表尝试了类似的查询(简单)架构。我将其修改为使用物化CTE方法,它在1秒内运行而不是18秒。请参阅下面的方法。这快10倍

with cte as (
select /*+materialize*/  max(amount_sold) from sales)
select prod_id,sum(amount_sold) from
sales
group by prod_id
having max(amount_sold) in(
select * from cte)
/

因此,在您的情况下,您将子查询实现为

with CTE as (
    SELECT /*+ materialize */ b.status_id FROM USER_ROLE b 
                                  WHERE (b.ACTIVE = 1 OR ( b.ACTIVE IN ( 0,3 )  
        AND SYSDATE BETWEEN b.effective_from_date AND b.effective_to_date 
                                        )) 
                                 )
)

并在主查询中选择FROM CTE

答案 3 :(得分:0)

所以你有一个目前需要16.5秒的查询,你希望它运行得更快。要做到这一点,你需要知道花费16,5秒的位置。 Oracle数据库配备非常好,因此您可以非常详细地了解它正在做什么。您可以查看我在OTN论坛上写的这个帖子:

http://forums.oracle.com/forums/thread.jspa?messageID=1812597

不知道你的时间花在哪里,所有的努力都只是猜测......

此致 罗布。