Oracle SQL CURSOR 更新与加入和提交
Posted
技术标签:
【中文标题】Oracle SQL CURSOR 更新与加入和提交【英文标题】:Oracle SQL CURSOR Update with Join and Commit 【发布时间】:2018-08-21 09:18:05 【问题描述】:我想用另一个表的值更新一个表。但是:我需要在每 50000 行之后提交一次。我不想讨论为什么,我知道创建新表而不是更新的技巧,但这不是一个选项。我只需要查询方面的帮助。
对于 x 行后的更新,我发现了这个:
DECLARE
i NUMBER := 0;
CURSOR G1 IS SELECT * FROM test_new
FOR UPDATE;
BEGIN
FOR c1 IN S1 LOOP
UPDATE test SET col1 = 'somevalue1'
WHERE CURRENT OF G1;
i := i + 1; -- Commits after every X number of records
IF i > 1000 THEN
COMMIT;
i := 0;
END IF;
END LOOP;
COMMIT;
END;
要使用另一个表更新一个表,此代码有效:
DECLARE
l_r_id test_new.id_customer%type;
l_r_name test_new.name%type;
i NUMBER := 0;
CURSOR CUR is select tnw.id_customer, tnw.name
from test_new tnw
, test tes
where tnw.id_customer = tes.id_customer
FOR UPDATE;
BEGIN
OPEN cur;
LOOP
FETCH cur
INTO l_r_id, l_r_name;
UPDATE test
set name = l_r_name
where test.id_customer = l_r_id;
i := i+1;
EXIT WHEN cur%notfound;
END LOOP;
commit;
END;
但我不知道如何获得
IF i > 50000 THEN
COMMIT;
i := 0;
END IF;
进入代码。看来 FETCH 和 Commit 有问题。我从 Oracle 收到错误消息:
2) 如果游标是使用 FOR UPDATE 子句打开的,则在发出 COMMIT 后获取将返回错误。
有人有想法吗?我知道有一种方法可以在没有“FETCH”的情况下加入,但我不知道如何。就像我之前说的,请只帮助代码,不要讨论更新和提交。
【问题讨论】:
Tom Kyte about frequent commits: "错了,错了,错了。大错特错....大错特错。" 可能是这样的? docs.oracle.com/en/database/oracle/oracle-database/12.2/lnpls/… 什么错误?此外,在您的第一个示例中,您的g1
光标是select from test_new for update
,但随后您更新了一个不同的表where current of g1
,我希望它提供ORA-01410: invalid ROWID
。在您的第二个示例中,我不清楚您为什么从 Cursor FOR 循环切换到更冗长且效率更低的 OPEN-FETCH-EXIT-CLOSE 语法。
老实说,我不知道如何加入第一个示例,而第二个示例完美运行。我知道 Tom Kyte 对这个话题有意见,他说得很清楚。我不会在 ETL-Job 中使用它,但对于一个良好且经过良好测试的一次性更新,它非常有帮助。
@WilliamRobertson 是正确的.. for update
和 current of
应该在同一张桌子上..
【参考方案1】:
Exit
必须在fetch
之后,如果游标没有数据,则循环终止。
在循环结束时,您可以按如下方式查询您的提交。 所以你的循环可以是这样的
LOOP
FETCH cur
INTO l_r_id, l_r_name;
EXIT WHEN cur%notfound;
UPDATE test
set name = l_r_name
where test.id_customer = l_r_id;
i := i+1;
if mod(i,50000) = 0 then
commit;
end if;
END LOOP;
另一个建议是定义记录集合。
使用表 test_new
中的数据填充集合。然后迭代这个集合并进行更新。
如果不进行测试,解决方案可能如下所示。
DECLARE
TYPE tabTest IS TABLE OF test_new%ROWTYPE;
t_test tabTest;
i NUMBER := 0;
BEGIN
select tnw.*
bulk collect into t_test
from test_new tnw
, test tes
where tnw.id_customer = tes.id_customer
for indx in 1 .. t_test.count()
loop
UPDATE test
set name = t_test(indx).name
where test.id_customer = t_test(indx).id;
i := i+1;
if mod(i,50000) = 0 then
commit;
end if;
end loop;
commit;
END;
【讨论】:
第一个例子得到同样的错误:2) 如果游标是用 FOR UPDATE 子句打开的,在发出 COMMIT 后获取将返回错误。" 但是通过第二个和一些更改它可以工作。 t_test.count() 不起作用,但我设置了一个带有计数的变量。【参考方案2】:我在@hotfix 帮助下的解决方案:
DECLARE
TYPE tabTest IS TABLE OF test_new%ROWTYPE;
t_test tabTest;
testcount number;
i NUMBER := 0;
BEGIN
select count(*) into testcount from test_new;
select tnw.*
bulk collect into t_test
from test_new tnw
, test tes
where tnw.id_customer = tes.id_customer;
FOR cur IN 1..testcount LOOP
UPDATE test
set name = t_test(cur).name
where test.id_customer = t_test(cur).id_customer;
i := i + 1; -- Commits after every X number of records
IF i > 50000 THEN
COMMIT;
i := 0;
END IF;
END LOOP;
COMMIT;
END;
【讨论】:
以上是关于Oracle SQL CURSOR 更新与加入和提交的主要内容,如果未能解决你的问题,请参考以下文章
Oracle - 使用 CURSOR LOOP 进行批量更新
[ORACLE]自适应游标共享Adaptive Cursor Sharing