Оптимизация Oracle Query - PullRequest
       4

Оптимизация Oracle Query

0 голосов
/ 29 июня 2010

Выполнение плана объяснения по этому запросу. Я получаю полный доступ к таблице.

Используются две таблицы:

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 ]

3 голосов
/ 29 июня 2010

Во-первых, у вас есть подзапрос

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 )
      )

Нет способа сделать что-либо кроме полного сканирования таблицы, чтобы получить такой результат. Возможно, вы пропустили объединение, но не знаете, чего ожидает ваш запрос, и мы не сможем сообщить вам.

Во-вторых, в зависимости от пропорции записей cmp_role с group_id, равным 3, и доли user_role, соответствующей этим ролям, может быть лучше выполнить полное сканирование там. Если, скажем, 3 из 27 записей cmp_role находятся в группе 3, и 100 000 записей user_role соответствуют этим записям cmp_role, то может быть более эффективно выполнить одно сканирование таблицы, чем 100 000 просмотров индекса.

0 голосов
/ 29 июня 2010

Итак, у вас есть запрос, который в настоящее время занимает 16,5 секунд, и вы хотите, чтобы он выполнялся быстрее. Для этого нужно знать, на что потрачены эти 16,5 секунд. База данных Oracle чрезвычайно хорошо оснащена, поэтому вы можете очень подробно увидеть, что она делает. Вы можете проверить эту тему, которую я написал на OTN форумах:

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

Не зная, где тратится ваше время, все усилия - лишь догадки ...

С уважением, Роб.

0 голосов
/ 29 июня 2010

Я думаю, что следующий подход будет работать. Я мог бы подумать, что подзапрос будет оцениваться только один раз, поскольку он не коррелирован - это не так. схемы. Я изменил его, чтобы использовать подход Материализованного 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 
                                        )) 
                                 )
)

и выберите ОТ ОТ CTE в основном запросе

0 голосов
/ 29 июня 2010
  1. Соберите статистику для таблиц
  2. объясните план запроса и покажите результаты.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...