即使在查询改变之后,Oracle Query也会陷入同一个地方

时间:2011-09-05 17:25:03

标签: sql oracle sql-update sqlplus

这已经困扰了我大约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有一个MONKEYS(因此MONKEY_PUZZLES.GIRAFFE_ID 1可能与MONKEY.MONKEY_ID 1,2和334匹配)。

我想根据MONKEY_PUZZLES.MONKEY_PUZZLE_ID字段设置MONKEYS.MONKEY_PUZZLE_ID字段,因为当前我的MONKEYS.MONKEY_PUZZLE_ID字段为空。我有索引:

  • MONKEYS.MONKEY_ID(带索引的主键)
  • MONKEYS.GIRAFFE_ID
  • MONKEY_PUZZLES.MONKEY_PUZZLE_ID(带索引的主键)
  • MONKEY_PUZZLES.GIRAFFE_ID

我在MONKEYS表中还有超过160万行,在MONKEY_PUZZLES表中有超过50,000行。

我最初使用以下查询:

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%并废弃。

所以我认为一定是我的剧本。我回到原点,吃了一根香蕉,说了很多“Oook”并提出了以下变化脚本,这更加明确:

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%,我会在早上更新。

编辑:最终只用了57分钟就达到了100%(所以不是那么糟糕),但考虑到它在2分钟内达到了92.67%,那真是太可怕了!

2 个答案:

答案 0 :(得分:4)

我对您所看到的具体症状没有任何解释 - 可能更新的执行计划有助于解释它 - 但无论如何我希望这会有更好的表现:

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 :(得分:1)

如果您遇到类似这样的问题,那么最简单的事情就是完全忽略它。 NB无法解决问题的原因!对于这个查询和你的查询,你应该在monkey_puzzles中有一个关于giraffe_id的索引; 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;
/