在程序中使用FOR UPDATE时应该何时提交?

use*_*er1 2 oracle plsql stored-procedures oracle10g oracle11g

如果我在存储过程中使用FOR UPDATE子句应该何时"提交"?关闭打开的光标后或关闭打开的光标之前?以下是我正在使用的程序,我是以正确的方式做的吗?

CREATE OR REPLACE PROCEDURE Proc_UpdateCSClientCount(inMerid     IN  VARCHAR2,
                                                     outCliCount  OUT NUMBER,
                                                     outretvalue  OUT NUMBER)
AS
   CURSOR c1 IS
      SELECT CLIENT_COUNT
        FROM OP_TMER_CONF_PARENT
       WHERE MER_ID = inMerid
      FOR UPDATE OF CLIENT_COUNT;
BEGIN
   OPEN c1;
   IF SQL%ROWCOUNT = 1 THEN
      FETCH c1 INTO outCliCount;
      outCliCount := outCliCount + 1;
      UPDATE OP_TMER_CONF_PARENT
         SET CLIENT_COUNT = outCliCount
       WHERE CURRENT OF c1;
   END IF;
   outretvalue := 0;
   CLOSE c1;
   COMMIT;
EXCEPTION
   WHEN no_data_found THEN
      outretvalue := -1;
END;
Run Code Online (Sandbox Code Playgroud)

Vin*_*rat 6

您应该在交易结束时提交.我怀疑你能找到一个合理的情况,即交易结束处于FOR UPDATE循环中间.

也许你听说经常提交是一件好事.这是一个虚假的神话,这是完全错误的.在Oracle中则相反:提交涉及额外的工作,因此您应该只在所有工作完成时提交,而不是之前.

此外,从逻辑的角度来看,如果你可以从头开始而不是完成一半的工作,那么从错误中恢复是不可想象的.

IMO,提交程序应该是非常罕见的.调用应用程序应该是进行必要检查的应用程序,最后决定是否应该提交数据.

总之,你不能在FOR UPDATE循环中提交(它会生成一个ORA-01002: fetch out of sequence),这是一件好事.每当你发现自己正在进行正常循环时,你应该问自己是否真的需要提交 - 很可能不是.

如果你真的需要提交并且只获取一次,那么在关闭游标之前或之后提交都没关系.


根据您的代码摘录进行更新:您的代码中有许多需要更正的内容(我认为它不是直接生产代码,但仍然是):

  • 永远不会引发异常:只能隐式SELECT INTO生成NO_DATA_FOUND.
  • SQL%ROWCOUNT如果前面的语句是a,则为NULL SELECT.
  • 您可以使用c1%ROWCOUNT,但这只会返回获取的行数:0在初始之后open.
  • 我主要使用FOR UPDATE NOWAIT这样两个会话永远不会互相阻塞.如果你只使用FOR UPDATE,你也可以使用一个,UPDATE而不是SELECT事先使用.
  • 这是一个优先考虑的问题,但返回代码容易出错,通常首选异常.让错误传播.为什么有人会在id不存在的情况下调用此函数?这可能是调用app/procedure中的一个错误,所以你不应该抓住它.

所以你可以像这样重写你的程序:

CREATE OR REPLACE PROCEDURE Proc_UpdateCSClientCount(inMerid     IN  VARCHAR2, 
                                                     outCliCount OUT NUMBER) AS
BEGIN
   -- lock the row, an exception will be raised if this row is locked
   SELECT CLIENT_COUNT + 1
     INTO outCliCount
     FROM OP_TMER_CONF_PARENT
    WHERE MER_ID = inMerid
   FOR UPDATE OF CLIENT_COUNT NOWAIT;
   -- update the row
   UPDATE OP_TMER_CONF_PARENT
      SET CLIENT_COUNT = CLIENT_COUNT + 1
    WHERE MER_ID = inMerid;
END;
Run Code Online (Sandbox Code Playgroud)