Oracle Query застревает в том же месте даже после изменения запроса - PullRequest
2 голосов
/ 05 сентября 2011

Это беспокоило меня около 4 часов, поэтому я подумал, что пришло время обратиться за помощью. Я не могу найти ничего похожего в Интернете, но в основном потому, что значения довольно специфичны, и я не совсем уверен, что искать ...

У меня проблема со сценарием Oracle, работающим в SQLPlus 10.2.0.5.

Проблема:

(имена и реальные данные были изменены для защиты личности подозреваемых) У меня есть таблица с именем MONKEYS и таблица с именем MONKEY_PUZZLES, которая выглядит примерно так:

MONKEYS

  • MONKEY_ID
  • GIRAFFE_ID
  • MONKEY_PUZZLE_ID

MONKEY_PUZZLES

  • MONKEY_PUZZLE_ID
  • GIRAFFE_ID

MONKEY_PUZZLES.GIRAFFE_ID и MONKEYS.GIRAFFE_ID совпадают, но для MONKEY_PUZZLE существует n MONKEYS (поэтому MONKEY_PUZZLES.GIRAFFE_ID 1 может соответствовать MONKEY.MONKEY_ID 1, 2 и 334).

Я хочу установить поле MONKEYS.MONKEY_PUZZLE_ID на основе поля MONKEY_PUZZLES.MONKEY_PUZZLE_ID, потому что в настоящее время мое поле MONKEYS.MONKEY_PUZZLE_ID равно нулю. У меня есть индексы на:

  • MONKEYS.MONKEY_ID (первичный ключ с индексом)
  • MONKEYS.GIRAFFE_ID
  • MONKEY_PUZZLES.MONKEY_PUZZLE_ID (первичный ключ с индексом)
  • MONKEY_PUZZLES.GIRAFFE_ID

У меня также есть более 1,6 миллиона строк в таблице MONKEYS и более 50000 строк в таблице MONKEY_PUZZLES.

Первоначально я использовал следующий запрос:

UPDATE MONKEYS M SET M.MONKEY_PUZZLE_ID = 
  (SELECT MP.MONKEY_PUZZLE_ID FROM MONKEY_PUZZLES MP 
   WHERE M.GIRAFFE_ID = MP.GIRAFFE_ID 
   AND MP.GIRAFFE_ID IS NOT NULL);

Тем не менее, этот сценарий может занять около 2 минут, чтобы выполнить от 0% до 92,67%, затем потребовалось более 25 минут, чтобы выполнить 94%. Я в конце концов остановил сценарий. Я запускал его несколько раз (я играл с разными индексами и DBMS_STATS), но каждый раз он доходил до 92,67% и вылетал.

Так что я подумал, что это мой сценарий. Я вернулся на круги своя, съел банан, много сказал «Оук» и придумал следующий вариантный сценарий, который гораздо более явный:

UPDATE MONKEYS M2 SET M2.MONKEY_PUZZLE_ID = 
  (SELECT X.MPID FROM 
    (SELECT M.MONKEY_ID MID, MP.MONKEY_PUZZLE_ID MPID 
     FROM MONKEYS M INNER JOIN MONKEY_PUZZLES MP 
     ON MP.GIRAFFE_ID = M.GIRAFFE_ID) X 
  WHERE X.MID = M2.MONKEY_ID);

Однако это был мусор. Даже с моими индексами потребовалось более 5 минут, чтобы добраться до 2%, поэтому я отменил это.

Затем я предложил следующий вариант, которым я был очень доволен:

UPDATE MONKEYS M SET M.MONKEY_PUZZLE_ID = 
  (SELECT MP.MONKEY_PUZZLE_ID 
   FROM MONKEY_PUZZLES MP 
   WHERE M.GIRAFFE_ID = MP.GIRAFFE_ID 
   AND EXISTS 
     (SELECT 1 FROM MONKEY_PUZZLES MP2 
      WHERE M.GIRAFFE_ID = MP2.GIRAFFE_ID));

Однако, и я действительно не мог поверить своим глазам, этот сценарий вел себя почти так же, как первый; работает примерно за 2 минуты, от 0% до 92,67%, а затем занимает совсем много времени, чтобы продолжить. Что это с 92,67%?!

Общее количество блоков составляет 41 717, поэтому до 38 000 блоков он достигает значительного замедления.

На случай, если вам интересно, я использую следующий запрос в отдельном сеансе SQLPlus, чтобы вычислить% времени выполнения:

SELECT X.*, TO_CHAR(SYSDATE, 'HH24:MI:SS') TIMESTAMP 
  FROM (select sid, serial#, opname, sofar, totalwork, 
    round(sofar/totalwork*100,2) "% Complete" from v$session_longops) X 
  WHERE "% Complete" < 100 and totalwork > 0;

(который является вариацией этого: http://searchoracle.techtarget.com/tip/Tracking-the-progress-of-long-running-queries)

Пожалуйста, помогите избавить бедного тигра от страданий!

P.S. Я собираюсь оставить скрипт работающим всю ночь, а утром обновлю, если он достигнет 100%.

РЕДАКТИРОВАТЬ: он достиг 100% в конечном итоге всего через 57 минут (так что не все так плохо), но, учитывая, что он достиг 92,67% за 2 минуты, это довольно ужасно!

Ответы [ 2 ]

4 голосов
/ 06 сентября 2011

У меня нет объяснения конкретных симптомов, которые вы видите - возможно, план выполнения обновления поможет объяснить это - но в любом случае я ожидаю, что это будет работать лучше:

MERGE INTO monkeys m
USING monkey_puzzles mp
ON (mp.giraffe_id = m.giraffe_id)
WHEN MATCHED THEN UPDATE SET m.monkey_puzzle_id = mp.monkey_puzzle_id
1 голос
/ 05 сентября 2011

Если у вас возникли проблемы с чем-то подобным, то самое простое, что можно сделать, это полностью проигнорировать. NB Не решает причину проблемы! Для этого и вашего запроса у вас действительно должен быть индекс giraffe_id в monkey_puzzles; Giraffe_id, monkey_puzzle_id будет еще лучше.

declare

  i number(10) := 0;

begin

  for xx in ( select rowid as rid, giraffe_id
                from monkeys ) loop

     UPDATE MONKEYS M 
        SET M.MONKEY_PUZZLE_ID = ( SELECT MP.MONKEY_PUZZLE_ID 
                                     FROM MONKEY_PUZZLES MP 
                                    WHERE MP.GIRAFFE_ID = xx.giraffe_id )
      WHERE rowid = xx.rid
            ;

     i := i + 1;
     if mod(i,1000) = 0 then
       commit;
       -- and some show-boating from memory (if you're using PL\SQL)
       dbms_application_info.set_module('Updating Monkeys', 'Total: ' || i );
     end if;

   end loop;
   commit;

end;
/
...