Oracle中的SELECT ... FOR UPDATE

时间:2018-12-02 19:00:57

标签: oracle plsql

我为更新语句创建了PACKAGE和PACKAGE BODY,

我没有在StackOverFlow中找到与此相关的任何内容, 我想知道我做这件事的方式是否很好, 以及是否要更改任何内容。

如果我打电话给程序

begin
account_api.upd_account(1, 'user', 1000)
end;

在我用commit语句完成事务之前,应该锁定这些过程

CREATE OR REPLACE PACKAGE account_api AS
PROCEDURE upd_account
          (
            p_acc_id     accounts.acc_id%type
          , p_acc_name   accounts.acc_name%type
          , p_acc_amount accounts.acc_amount%type
      );
end account_api;
 /

CREATE OR REPLACE PACKAGE BODY account_api AS
PROCEDURE upd_account (
            p_acc_id     accounts.acc_id%type
          , p_acc_name   accounts.acc_name%type
          , p_acc_amount accounts.acc_amount%type
)
is
l_current_balance NUMBER;
l_new_balance NUMBER;

BEGIN

  SELECT acc_amount INTO l_current_balance
  FROM accounts
  WHERE acc_id = p_acc_id
  FOR UPDATE;

  l_new_balance := l_current_balance + p_acc_amount;

  IF l_new_balance < 0 THEN
      raise_application_error(-20001, 'Sorry you re overdrawn');
  END IF;

  UPDATE accounts
  SET acc_amount = l_new_balance,
      acc_name = p_acc_name
  WHERE acc_id = p_acc_id;
COMMIT;
end;
end account_api;
/

1 个答案:

答案 0 :(得分:4)

该过程对我来说似乎可以,但是我将其简化为仅一个语句,然后添加一个显式的ROLLBACK语句,如下所示:

  UPDATE accounts
  SET acc_amount = acc_amount + p_acc_amount,
      acc_name = p_acc_name
  WHERE acc_id = p_acc_id
  RETURNING acc_amount INTO l_new_balance;
  IF l_new_balance < 0 THEN
      rollback;
      raise_application_error(-20001, 'Sorry you re overdrawn');
  ELSE
      commit;
  END IF;

无需使用其他SELECT ... FOR UPDATE来锁定行,因为UPDATE语句也锁定了行。感谢RETURNING INTO子句,您可以检索新的余额,然后在更新后检查它,如果新的平衡太低,则回滚更改,否则将其提交。