只用光标选择行一次
Posted
技术标签:
【中文标题】只用光标选择行一次【英文标题】:Select rows with cursor once only 【发布时间】:2014-04-10 10:47:41 【问题描述】:我在 PL/SQL 中有一种情况,我的过程每分钟都会被作业调用一次。代码如下
procedure ExecuteProc is
CURSOR c1 IS
SELECT *
FROM test6
where processed = 'N';
BEGIN
-- Make all rows selected in the cursor processed 'Y' but
-- the cursor below would fail
for rec in c1
loop
-- some proccessing which takes 5 -6 minutes
delete from test6 where id = rec.id;
end loop;
END;
test6 表中的示例数据
id processed
1 N
2 N
3 N
第一次调用过程时,id 1,2 和 3 被游标中的过程拾取。请注意这些行也存在于表 test6 中,除非每个 id 的处理都已完成。这意味着在下一分钟第二次调用该程序时,该程序会再次获取 1,2 和 3。我怎样才能避免这种情况?我正在考虑以某种方式将“已处理”字段设置为“Y”,但是(请参阅我在光标失败的代码中的注释)。
请帮忙
【问题讨论】:
首先,当执行的动作需要5-6分钟时,至少有人怀疑该工作每分钟完成一次。在我看来,这会导致很多会话锁定,并且等待时间会增加。有没有办法优化“-一些需要5 -6分钟的处理”?流程是什么? 【参考方案1】:删除前尝试锁定行:
procedure ExecuteProc is
CURSOR c1 IS
SELECT *
FROM test6
where processed = 'N';
BEGIN
-- Make all rows selected in the cursor processed 'Y' but
-- the cursor below would fail
for rec in c1
loop
if rec.processed <> 'L' then
-- some proccessing which takes 5 -6 minutes
update test6 set processed ='L' where id = rec.id; -- lock status
delete from test6 where id = rec.id;
end if;
end loop;
END;
您一定会删除正确的。 但是你必须优化,调用你处理的后面的程序。
【讨论】:
我认为由于旧的 MUTEX(互斥)问题,这将在多线程中崩溃。您无法保证两个线程不会进入同一行的循环,并且同一行会执行两次大处理。在处理之前,您必须保证该行仅由一个线程标记,这只能通过 NOWAIT 或 SKIP LOCKED...【参考方案2】:如果您使用的是 11g,那么您很幸运 :-) 您可以在光标中使用 SELECT FOR UPDATE SKIP LOCKED - 这样您就可以保证两个线程在一次提取时永远不会获得相同的行!
在 Oracle 中执行此操作的正确方法是高级队列 - 因为这将解决您的所有问题,提供同步和并行执行以及其他好处...
在 Oracle 11 之前并且没有高级队列(如果你真的想自己编写代码......)你可以这样做: (标有自治事务的位必须作为自治事务在单独的方法中调用。如果更新返回 0 行,则可能是因为表为空,也可能是因为两个线程访问了同一行。-所以我们需要一个单独的结束条件计算可用行数。
-- Example if you CAN NOT use AQ or UPDATE SKIP LOCKED
LOOP
-- ### autonomous transaction: (can be in its own method returning the id)
BEGIN
-- Mark a single ROW as being in processing...
UPDATE test6 SET processed = 'L' WHERE processed = 'N' AND ROWNUM = 1 NOWAIT
RETURNING id INTO l_id;
EXCEPTION WHEN NO_DATA_FOUND THEN ... -- Can possibly fire if two threads access the same row...
l_id := NULL;
END
-- ### end autonomous transaction;
IF l_id IS NOT NULL THEN
--DO BIG PROCESSING
-- Mark the ROW is processed and DONE!
UPDATE test6 SET processed = 'Y' WHERE id = l_id;
COMMIT;
END;
SELECT COUNT(*) FROM test6 WHERE processed = 'N' INTO rowsleft;
EXIT IF rowsleft = 0;
END;
【讨论】:
以上是关于只用光标选择行一次的主要内容,如果未能解决你的问题,请参考以下文章
从表中选择不同的记录并执行重复行的列总和(托盘、总和)。并显示重复的行一次[关闭]
RXJS combineLatest running pipe 7 次一次发射