Как я могу оптимизировать этот набор запросов оракула

Я просматриваю некоторый код pl/sql без комментариев. Пытаюсь разобраться и оптимизировать. Вот образец:

INSERT INTO gtt1 --75711 rows
(USER_ID, role_id, participant_code, status_id )
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;

Затем

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

наконец (это занимает больше всего времени)

OPEN cv_1 FOR

SELECT c.role_id,
       c.subgroup,
       c.subgroup_description,
       COUNT(a.USER_ID) user_count
FROM   
    (SELECT b.user_id, b.role_id FROM gtt1 b, pt_user e
    --pt_user table has 73000 rows
        WHERE  e.user_id = RTRIM(b.user_id)
       ) a
RIGHT OUTER JOIN CMP_ROLE c ON a.role_id = c.role_id
WHERE c.group_id = v_group_id
GROUP BY c.role_id,c.subgroup,c.subgroup_description
ORDER BY c.subgroup;

Есть ли способ избежать удаления из gtt1 и сначала просто получить нужные строки?

Запустив explain plan, я заметил несколько полных сканирований таблицы по этому запросу:

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

user_role = 803507 строк

cmp_role = 27 строк

user_role имеет 5 индексов:

idx 1 = идентификатор_роли

idx 2 = last_updt_user_id

idx 3 = id_активности, код_участника, дата_действия_от_даты, дата_действия_до_даты

idx 4 = идентификатор_пользователя, идентификатор_роли, дата_действия_от_даты, дата_действия_до_даты

idx 5 = код_участника, идентификатор_пользователя, roke_id, actv_cd


person Mehur    schedule 28.06.2010    source источник
comment
Всегда ли gtt1 одинаков для ваших запросов? Это последовательные запросы? Может быть, вы могли бы попробовать записать их в один вместо трех запросов?   -  person N. Gasparotto    schedule 28.06.2010


Ответы (2)


INSERT и DELETE кажутся мне эквивалентными этому:

INSERT INTO gtt1 --75711 rows
(USER_ID, role_id, participant_code, status_id )
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 UIV_CMP_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
                                  ))
                           );

Я понятия не имею, будет ли это более эффективным - мне нужно знать намного больше о таблицах, индексах и данных.

Затем вы можете пойти дальше и превратить GTT в подзапрос, например:

WITH gtt1 AS 
  (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 UIV_CMP_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 c.role_id,
       c.subgroup,
       c.subgroup_description,
       COUNT(a.USER_ID) user_count
FROM   
    (SELECT b.user_id, b.role_id FROM gtt1 b, pt_user e
    --pt_user table has 73000 rows
        WHERE  e.user_id = RTRIM(b.user_id)
       ) a
RIGHT OUTER JOIN CMP_ROLE c ON a.role_id = c.role_id
WHERE c.group_id = v_group_id
GROUP BY c.role_id,c.subgroup,c.subgroup_description
ORDER BY c.subgroup;

Опять же, я понятия не имею, является ли это более или менее эффективным, чем текущий код.

person Tony Andrews    schedule 28.06.2010
comment
Тони, комбинированный запрос WITH AS работает отлично, но мне все еще нужно сэкономить время. Я предоставляю строки во всех таблицах. не могли бы вы посмотреть, можно ли его где-нибудь оптимизировать? - person Mehur; 29.06.2010
comment
Я не удивлен, что запрос включает в себя несколько полных сканирований: cmp_role имеет только 27 строк, поэтому полное сканирование имеет смысл, и полное сканирование user_role МОЖЕТ также иметь смысл, если многие из его строк будут соответствовать критериям, что код участника не равен нулю. и group_id = 3 (кстати, сколько строк user_role удовлетворяют этим критериям?) - person Tony Andrews; 29.06.2010
comment
При выполнении этого запроса возвращается 39392 строки, и это занимает 19 секунд. - person Mehur; 29.06.2010
comment
Сколько строк будет возвращено, если убрать предложение HAVING? - person Tony Andrews; 29.06.2010
comment
Хорошо, это справедливое число, и, возможно, поэтому Oracle считает, что не стоит использовать какой-либо индекс. Я вижу, вы начали еще один вопрос о настройке запроса, поэтому, вероятно, не стоит продолжать его здесь. - person Tony Andrews; 29.06.2010
comment
@tony, я не нашел решения для оптимизации этого запроса. Можете ли вы предложить какие-то способы объединения этого в одном соединении. Я могу сравнить результаты, чтобы увидеть, соответствуют ли они исходному запросу. - person Mehur; 30.06.2010
comment
Не совсем, без понимания данных намного больше. Я нахожу всю часть MAX(status_id) озадаченной - что важно в максимальном значении status_id? - person Tony Andrews; 30.06.2010

Вы хотите оптимизировать сам запрос или только время, необходимое для получения результатов? Во-вторых, потребуются некоторые данные о количестве строк и так далее...

person poeschlorn    schedule 28.06.2010
comment
только время, необходимое для результатов. Я предоставлю более подробную информацию о количестве строк для каждой используемой таблицы. - person Mehur; 28.06.2010