Oracle SQL - 使事务成为原子

时间:2011-10-26 13:59:23

标签: oracle stored-procedures plsql

我有一个这样的程序:

create or replace
PROCEDURE NEWJOBIDPROC (JOB_ID OUT NUMBER )
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
-- PROCEDURE TO RETRIEVE THE JOB ID
--++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
IS
BEGIN
  -- select the job_id
    SELECT VALUE+1 INTO JOB_ID FROM JOB_TABLE WHERE ID = 50;
  -- update table JOB_TABLE with the latest job id
    UPDATE JOB_TABLE SET VALUE = JOB_ID WHERE ID = 50;

END;

现在我的问题如下。

让我们说我同时多次调用此过程。 在我们的示例中,让我们对同一过程进行两次同时调用。 当它们都运行select语句时,它们会收到一些值 - 让它为200.

现在,他们将对job_table进行更新,使用相同的值200 - 这不是我想要的。我不想要重复。

那么,我如何将整个代码标记为原子?我希望select和update能够同时运行并且是线程安全的。我希望将两个语句一起标记为原子。

3 个答案:

答案 0 :(得分:8)

请参阅@derobert提供的链接以获取更多信息,但是对于您的特定示例,您可以执行此操作:

UPDATE JOB_TABLE SET VALUE = VALUE +1 WHERE ID = 50
 RETURNING VALUE INTO JOB_ID;

但是 - 您是否考虑过使用序列?

答案 1 :(得分:2)

如果您打算在以后更新它,则应该锁定该行。第二个事务将被阻止并等待您完成事务(默认情况),或者如果它试图锁定指定了NOWAIT的行,则会收到错误。

BEGIN
  -- select the job_id and LOCK the row so that noone else can modify it
    SELECT VALUE+1 INTO JOB_ID FROM JOB_TABLE WHERE ID = 50 FOR UPDATE NOWAIT;
  -- update table JOB_TABLE with the latest job id
    UPDATE JOB_TABLE SET VALUE = JOB_ID WHERE ID = 50;    
END;

在任何一种情况下,锁定该行都会阻止您所描述的"lost update"行为。

答案 2 :(得分:1)

虽然序列可能是您问题的最佳解决方案,但我会使用乐观锁定来解决这类问题。 在JOB_TABLE中添加timestamp列时,可以在select查询中获取时间戳,并将其作为更新查询的where子句的约束添加。