需要帮助优化oracle查询

时间:2010-03-30 20:35:55

标签: oracle optimization

我需要帮助优化以下查询。这需要很长时间才能完成。这需要将近213秒。由于一些限制,我无法添加索引,必须与现有索引一起使用。

INSERT INTO temp_table_1
( USER_ID, role_id, participant_code, status_id )
WITH A AS
 (SELECT USER_ID user_id,ROLE_ID, STATUS_ID,participant_code
  FROM    USER_ROLE WHERE   participant_code IS NOT NULL), --1
B AS
 (SELECT ROLE_ID
  FROM    CMP_ROLE
  WHERE   GROUP_ID = 3),
C AS (SELECT USER_ID FROM USER) --2

SELECT USER_ID,ROLE_ID,PARTICIPANT_CODE,MAX(STATUS_ID)
FROM A INNER JOIN B USING (ROLE_ID)
       INNER JOIN C USING (USER_ID)
GROUP BY USER_ID,role_id,participant_code ;

--1 = query when ran alone takes 100+ seconds

--2 = query when ran alone takes 19 seconds

DELETE temp_table_1
WHERE ROWID NOT IN
( SELECT a.ROWID
  FROM temp_table_1 a,
  USER_ROLE b
  WHERE a.status_id = b.status_id
  AND   ( b.ACTIVE IN ( 1 ) OR ( b.ACTIVE IN ( 0,3 ) 
  AND SYSDATE BETWEEN b.effective_from_date AND b.effective_to_date ))
);

编写查询的人似乎首先尝试将所有内容放入临时表,然后从临时表中删除记录。剩下的就是实际的结果。

不能这样做,不需要删除吗?我们只是得到了所需的结果,因为这样可以节省时间吗?

3 个答案:

答案 0 :(得分:2)

这是一个天真地结合上述两个查询的查询,因此请确保检查并比较两种方法的输出。

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.active in (0,1,3)
  and r.participant_code is not null
  and sysdate between r.effective_from_date and r.effective_to_date
  and c.group_id = 3
group by 
  r.user_id, r.role_id, r.participant_code;

没有必要使用临时表,然后删除记录以获得所需的结果。虽然,可能有其使用的原因,也许表现?

此外,由于USER可以从USER_ID获得,因此查询和加入USER_ROLES表格似乎是不必要的。我在上面的查询中省略了它。希望这能为你提供一个良好的开端。

答案 1 :(得分:0)

在现有代码中删除之后,这应该在语义上等同于临时表中的左边的集合。虽然我同意AR,但不需要User表,除非它包含的user_id比user_role少。否则它不会以任何方式限制集合。如果User包含的user_id比user_role多,则不会更改结果集。 User_role是此查询中的主要驱动程序,具有来自cmp_role表的小限制。

select a.user_id,
       a.role_id,
       a.participant_code,
       a.status_id
  from (select a.user_id,
               a.role_id,
               a.participant_code,
               max(status_id) status_id
          from user_role a,
               (select role_id
                  from cmp_role
                 where group_id = 3
               ) b
         where a.participant_code is not null
           and a.active in (0, 1, 3)
           and sysdate between a.effective_from_date and a.effective_to_date
           and a.role_id = b.role_id
         group by a.user_id,
                  a.role_id,
                  a.participant_code
       ) a
       user c
 where a.user_id = c.user_id;

如果性能仍然很差,则可能会查看用于限制数据的某些字段的索引(user_role.role_id,user_role.participant_code,user_role.active,user_role.effective_from_date,user_role.effective_to_date)。

当然,需要一个解释计划或跟踪来全面了解Oracle在根据您的数据和结构执行此查询时所做的工作。

答案 2 :(得分:0)

让我们指出一些显而易见的事情。

  

- 1 =单独运行时的查询需要100多秒

     

- 2 =单独运行时查询需要19秒

USER表上的全表扫描不应该花费19秒。 USER_ROLE表上的全表扫描不应超过100秒,即使它有数千万行。当然,如果你真的有两千万用户,那么这些时间稍微不那么不合理,但仍然不能接受。

您需要了解为什么系统需要这么长时间才能进行简单的查找。 EXPLAIN PLAN希望我们理解连接,但这不会解决您的核心问题:为什么检索USER_ROLE数据需要这么长时间?这是一个复杂的观点吗?它有数以亿计的查询吗?您是否有PARTICIPANT_CODE的索引对此查询没有帮助?

使用这些表的其他查询怎么样?它们也有问题吗?如果是这样,您需要进行更多调查。无论是系统花了太长时间做某事还是等待某些资源。您需要做的是针对此查询运行10046跟踪并确定时间的位置。此跟踪将报告您的会话的等待事件。这将为您提供一些有关进展的正确信息。它比猜测要好得多。

自从9i以来,Oracle已经公开了Wait Interface。罗杰施拉格写了一篇相当不错的介绍。 Read it now。 (如果你是10g或更高,你也应该阅读his follow-up article)。