【问题标题】:Oracle Query gets Stuck in the Same Place Even After Query is Altered即使在查询被更改后,Oracle 查询也会卡在同一个地方
【发布时间】:2011-09-05 17:25:03
【问题描述】:

这已经困扰了我大约 4 个小时,所以我认为是时候寻求帮助了。我在网上找不到类似的东西,但主要是因为这些值非常具体,我不太确定要寻找什么......

这是我在使用 SQLPlus 10.2.0.5 中运行的 Oracle 脚本时遇到的问题。

问题:

(姓名和真实数据已更改以保护嫌疑人的身份) 我有一个名为 MONKEYS 的表和一个名为 MONKEY_PUZZLES 的表,它们看起来有点像这样:

猴子

  • MONKEY_ID
  • GIRAFFE_ID
  • MONKEY_PUZZLE_ID

MONKEY_PUZZLES

  • MONKEY_PUZZLE_ID
  • GIRAFFE_ID

MONKEY_PUZZLES.GIRAFFE_ID 和 MONKEYS.GIRAFFE_ID 匹配,但每个 MONKEY_PUZZLE 有 n 个 MONKEY(因此 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

请帮助一只可怜的老虎摆脱痛苦!

附:我打算让脚本在一夜之间运行,如果达到 100%,我会在早上更新。

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

【问题讨论】:

  • 您应该做几件事:为您的表创建优化器统计信息并获取相应语句的执行计划。此外,您应该检查查询卡住时的等待事件。
  • 执行计划在这里真的很有帮助。另外,您所有的 monkeys.monkey_puzzle_id 字段都是 NULL,还是添加 WHERE monkey_puzzle_id IS NULL 可能有帮助?
  • 谢谢@eaolson。我所有的monkeys.monkey_puzzle_ids 都是空的。 @steve,我使用DBMS_STATS.GENERATE_SCHEMA_STATS(OWNNAME=&gt;'JUNGLE',OPTIONS=&gt;'GATHER AUTO') 来生成关于我设置的索引的统计信息。还有什么可以帮助的吗?
  • 你伪装的表名让我很兴奋。

标签: sql oracle sql-update sqlplus


【解决方案1】:

我无法解释您看到的具体症状——更新的执行计划可能会帮助解释它——但无论如何我希望这会表现得更好:

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

【讨论】:

  • 嗨@DaveCosta,谢谢,我一定会试一试的。我很惭愧地承认我以前没有使用过 MERGE。只是出于兴趣,在SQLPlus中获取执行计划的方法是什么?我无权访问 GUI。我很想听听具体问题的解释,所以我希望以后能避免它。谢谢
  • 这太棒了。 17.31 秒!!我什至没有时间检查进度,太快了!我将来肯定会考虑MERGE。再次感谢。
  • @LordScree -- 查看执行计划的一种方法是 EXPLAIN PLAN (download.oracle.com/docs/cd/B19306_01/server.102/b14211/…)。如果可能,最好使用 DBMS_XPLAN.DISPLAY_CURSOR (download.oracle.com/docs/cd/B19306_01/appdev.102/b14258/…)。
【解决方案2】:

如果您遇到此类问题,那么最简单的做法就是完全忽略它。注意没有解决问题的原因!对于这个查询和你的查询,你真的应该在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;
/

【讨论】:

  • 感谢@Ben 的回复。我必须承认,我用过你的咒语太多次了!恐怕我尝试了您的循环脚本,但速度要慢得多(5 分钟后
猜你喜欢
  • 1970-01-01
  • 2013-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-11
  • 2015-02-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多