只用光标选择行一次

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;

【讨论】:

以上是关于只用光标选择行一次的主要内容,如果未能解决你的问题,请参考以下文章

从表中选择不同的记录并执行重复行的列总和(托盘、总和)。并显示重复的行一次[关闭]

一次一密加密解密算法

python将列表里的字符串一次一次显示出来

RXJS combineLatest running pipe 7 次一次发射

防止 MySQL 行一次更新两次? (nodejs mysql)

科学在一次一次的葬礼中进步