我试图在同一个表中的不同列上执行一些数学运算后,在大约200万行上更新一列空值。基本上,我将分钟从一列转换为秒,并根据某些条件使用秒更新空列。我没有光标就试过这个但是它花了太长时间并且没有完成。下面的脚本似乎也永远不会完成。这就是我到目前为止所做的:
DECLARE
CURSOR c1 IS
SELECT /*+ ORDERED USE_NL(g,e) */
e.event_code, e.time, e.period, e.time_elapsed, e.rowid
FROM table1.schedule s, table2.event e
WHERE e.event_code = s.event_code
AND s.schedule_id in (22,39,49,51,53,55,57,59,61,63,65,66,
68,69,71,72,75,77,78,80,82,84,86,87,89,92,93,95,97,98,
101,103,105,107,109,111,114,116,118,120,122,125,128,130,
133,135,137,140,50,52,54,56,58,60,62,64,67,70,73,74,76,79,
81,83,85,88,90,91,94,96,99,100,102,104,106,108,110,112,113,
115,117,119,121,123,124,126,127,129,131,132,134,136,138,141)
AND e.time_elapsed IS NULL
AND e.time IS NOT NULL
AND (e.period > 0 OR e.period < 0);
TYPE EventRecType IS RECORD (
Event_Code table1.schedule.event_code%type,
evTime table2.event.time%type,
evPeriod table2.event.period%type,
evTimeElapsed table2.event.time_elapsed%TYPE,
evRowId ROWID);
TYPE EventRecTab IS TABLE OF EventRecType INDEX BY PLS_INTEGER;
EventRec EventRecTab;
TYPE typ_evRecord IS RECORD (
eRowId ROWID,
TimeElapsed table2.event.time_elapsed%TYPE);
TYPE tab_evTable IS TABLE OF typ_evRecord INDEX BY PLS_INTEGER;
arr_evRecToUpdate tab_evTable;
BEGIN
OPEN c1;
LOOP
FETCH c1 BULK COLLECT INTO EventRec LIMIT 50000;
FOR k in 1..EventRec.count LOOP
if EventRec(k).evPeriod = 1 AND EventRec(k).evTime < 150 then
arr_evRecToUpdate(k).TimeElapsed := EventRec(k).evTime*60;
arr_evRecToUpdate(k).eRowId := EventRec(k).evRowId;
elsif EventRec(k).evPeriod = 2 AND EventRec(k).evTime < 150 then
arr_evRecToUpdate(k).TimeElapsed := (EventRec(k).evTime-45)*60;
arr_evRecToUpdate(k).eRowId := EventRec(k).evRowId;
elsif EventRec(k).evPeriod = 3 AND EventRec(k).evTime < 150 then
arr_evRecToUpdate(k).TimeElapsed := (EventRec(k).evTime-90)*60;
arr_evRecToUpdate(k).eRowId := EventRec(k).evRowId;
elsif EventRec(k).evPeriod = 4 AND EventRec(k).evTime < 150 then
arr_evRecToUpdate(k).TimeElapsed := (EventRec(k).evTime-105)*60;
arr_evRecToUpdate(k).eRowId := EventRec(k).evRowId;
elsif EventRec(k).evPeriod = 1 AND EventRec(k).evTime > 150 THEN
EventRec(k).evTime := ROUND(EventRec(k).evTime/60);
arr_evRecToUpdate(k).TimeElapsed := EventRec(k).evTime;
arr_evRecToUpdate(k).eRowId := EventRec(k).evRowId;
elsif (EventRec(k).evPeriod = 2) AND (EventRec(k).evTime > 150) THEN
EventRec(k).evTime := ROUND((EventRec(k).evTime/60)) + 45;
arr_evRecToUpdate(k).TimeElapsed := EventRec(k).evTime;
arr_evRecToUpdate(k).eRowId := EventRec(k).evRowId;
end if;
EXIT WHEN EventRec.COUNT() = 0;
END LOOP;
FORALL i_loopIndex IN 1 .. arr_evRecToUpdate.COUNT
UPDATE table2.event
SET time_elapsed = arr_evRecToUpdate(i_loopIndex).TimeElapsed
WHERE rowid = arr_evRecToUpdate(i_loopIndex).eRowid;
COMMIT;
END LOOP;
CLOSE c1;
END;
答案 0 :(得分:1)
您是SELECT
单独运行时查询消耗时间吗?
请阅读以下我的观点。它不会解决你的问题,但可以帮助你!
要考虑的要点:
1)您BULK COLLECT
收集到PL/SQL
集合中的所有数据都将放在PGA
中,并且它是静态的。如果使用集合的任何其他PL/SQL
块在后台运行,则所有块都将使用PGA
,并且它也不会共享。这绝对是一项代价高昂的操作,当集合非常庞大时,您可以在50K
批次中执行操作超过40
次。当然,您使用(subscript
)调用它们,这类似于使用index
查询表。
在发送到bulk bind
之前,PL/SQL engine
会消耗SQL Engine
本身的时间。您使用ROWID
的方法很棒。我不是说永远不应该使用PL / SQL集合。这取决于数据库的繁忙程度。您将在PGA中持有2M结果集,直到CURSOR耗尽为止!更好地与DBA交谈并尽可能增加PGA。
我的看法是在PGA中处理2M行,不太好。
2)正如评论中的某些人所说,拥有staging table
也是好的。我已经通过将其拆分为多个并行运行脚本来编写更新操作。这可能需要更多编码。
3)COMMIT
大小。这里COMMIT
的尺寸为50K
。提交大小越高,redo
/ undo
日志的大小就越大。这个表复制了吗?这些表格有triggers
?
4)有许多文件处理脚本可用(perl
)。如果可能的话,将查询数据下载到文件,进行文件处理并重新加载表格。(这可能有利于,当待更新的数量> =总数的50%时)
答案 1 :(得分:1)
更新表是一个缓慢的过程。这里的主要问题是,在更新行之前,您需要先执行一个查询。关于查询调优的所有常见问题都适用于此:基表中有多少行?将选择(更新)它们的百分比?它们如何分布在桌子上?你有什么指数?执行计划是什么样的?
因此,您需要先调整该查询。如果这是一次性的练习,也许建立一个指数是一个太大的投资,但我肯定会考虑建立一个:EVENTS(event_code, period, time_elapsed, time, event_primary_key)
否则。
我已成为MERGE的忠实粉丝,作为执行从多个表中提取数据的更新的一种方式,因为Oracle不支持除SELECT之外的其他任何内容的ANSI 92连接语法。
以下可能不太正确,但确实说明了一般原则。
merge into table2.event e
using ( SELECT e.event_code, e.time, e.period, e.time_elapsed, e.event_primary_key
, case
when e.evPeriod = 1 AND e.evTime < 150 then
e.evTime*60;
when e.evPeriod = 2 AND e.evTime < 150 then
(e.evTime-45)*60;
when e.evPeriod = 3 AND e.evTime < 150 then
(e.evTime-90)*60;
when e.evPeriod = 4 AND e.evTime < 150 then
(e.evTime-105)*60;
when e.evPeriod = 1 AND e.evTime > 150 THEN
ROUND(e.evTime/60);
when (e.evPeriod = 2) AND (e.evTime > 150) THEN
e.evTime := ROUND((e.evTime/60)) + 45;
end as TimeElapsed
FROM table1.schedule s, table2.event e
WHERE e.event_code = s.event_code
AND s.schedule_id in (22,39,49,51,53,55,57,59,61,63,65,66,
68,69,71,72,75,77,78,80,82,84,86,87,89,92,93,95,97,98,
101,103,105,107,109,111,114,116,118,120,122,125,128,130,
133,135,137,140,50,52,54,56,58,60,62,64,67,70,73,74,76,79,
81,83,85,88,90,91,94,96,99,100,102,104,106,108,110,112,113,
115,117,119,121,123,124,126,127,129,131,132,134,136,138,141)
AND e.time_elapsed IS NULL
AND e.time IS NOT NULL
AND (e.period > 0 OR e.period < 0) q
on (q.event_primary_key = e.event_primary_key)
when matched then
update
set e.time_elapsed = q.TimeElapsed;
答案 2 :(得分:1)
为什么使用此提示/*+ ORDERED USE_NL(g,e) */
?嵌套循环连接仅适用于小型表。大表通常由Hash-Join加入。
与其他答案一样,循环会降低您的操作速度。使用单个DML几乎在所有情况下都是禁食方式。
尝试这个,UPDATE (SELECT ... FROM ...) SET =
看起来有点不常见,但它运行正常。
UPDATE
(SELECT e.time_elapsed, e.evPeriod, e.evTime
FROM table1.schedule s
JOIN table2.event e ON e.event_code = s.event_code
WHERE s.schedule_id in (22,39,49,51,53,55,57,59,61,63,65,66,68,69,71,72,75,77,78,80,82,84,86,87,89,92,93,95,97,98,101,103,105,107,109,111,114,16,118,120,122,125,128,130,133,135,137,140,50,52,54,56,58,60,62,64,67,70,73,74,76,79,81,83,85,88,90,91,94,96,99,100,102,104,106,108,110,112,113,115,117,119,121,123,124,126,127,129,131,132,134,136,138,141)
AND e.time_elapsed IS NULL
AND e.time IS NOT NULL
AND (e.period > 0 OR e.period < 0)
)
SET time_elapsed =
case
when evPeriod = 1 AND evTime < 150 then
evTime*60
when evPeriod = 2 AND evTime < 150 then
(evTime-45)*60
when evPeriod = 3 AND evTime < 150 then
(evTime-90)*60
when evPeriod = 4 AND evTime < 150 then
(evTime-105)*60
when evPeriod = 1 AND evTime > 150 THEN
ROUND(evTime/60)
when (evPeriod = 2) AND (evTime > 150) THEN
ROUND((evTime/60)) + 45
end;