0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

FOR UPDATEで取得したレコードをそのまま更新する方法(Oracle Database )

Posted at

FOR UPDATE付きカーソルでのレコード更新方法と注意点

1. FOR UPDATEカーソル内でのUPDATE利用可否

SELECT ... FOR UPDATE句付きのカーソルは、取得した行に排他ロック(Exclusiveロック)をかけます。これにより、同一トランザクション内でフェッチした行を安全に更新できます。ループ内でレコードをUPDATEする方法として、主に次の2通りがあります。

  • WHERE CURRENT OF句を使用する方法:明示的に宣言したカーソル名がある場合、UPDATE テーブル SET ... WHERE CURRENT OF カーソル名 で現在フェッチしている行を直接更新できます。
  • キーを使ってUPDATEする方法:フェッチしたレコードの主キー(または行識別子)を使い、UPDATE テーブル SET ... WHERE 主キー = rec.主キー という形で更新します。これは暗黙カーソル(FOR rec IN (...) LOOP)でも同様に可能です。

2. REC変数を使ったUPDATE例

代表的なコード例を示します。

  • 主キーで指定してUPDATE:カーソルのRECから主キーを参照して更新する例です。

    FOR rec IN (
      SELECT employee_id, salary 
        FROM employees 
       WHERE department_id = 10 
         FOR UPDATE
    ) LOOP
      -- フェッチしたレコードの給与に10%加算して更新(主キーを使用)
      UPDATE employees
         SET salary = rec.salary * 1.10
       WHERE employee_id = rec.employee_id;
    END LOOP;
    COMMIT;
    
  • WHERE CURRENT OFを使用する方法:明示的カーソルを使い、WHERE CURRENT OF で更新する例です。

    DECLARE
      CURSOR cur IS
        SELECT employee_id, salary 
          FROM employees 
         WHERE department_id = 10 
           FOR UPDATE;
      v_emp_id employees.employee_id%TYPE;
      v_sal    employees.salary%TYPE;
    BEGIN
      OPEN cur;
      LOOP
        FETCH cur INTO v_emp_id, v_sal;
        EXIT WHEN cur%NOTFOUND;
        -- カーソルcurの現在行を更新(内部的にROWIDで更新される)
        UPDATE employees
           SET salary = v_sal * 1.10
         WHERE CURRENT OF cur;
      END LOOP;
      CLOSE cur;
      COMMIT;
    END;
    

これらの例では、前者はフェッチしたrec.employee_idをWHERE句で指定し、後者はカーソル名とCURRENT OFを使って現在行を更新しています。

3. ロックの影響(同時実行性への注意)

  • 通常のSELECTFOR UPDATE無し)はデータロックを取得しません。そのため、他セッションから同じテーブルや行を参照・更新でき、クエリは常にブロックされません。
  • 一方、SELECT ... FOR UPDATEは該当行に排他ロックをかけます。これにより、他のセッションはその行を更新・削除できず、ロック解除(コミットまたはロールバック)まで待機が発生します。
  • 行ロックは通常トランザクションの終了まで保持されるため、長時間コミットを遅らせると同時実行性能が低下します。大量の行をロックすると他処理の並列性が落ちるため、必要以上の行ロックは避けるべきです。

4. カーソル更新時の注意点

  • WHERE CURRENT OF句の利用条件WHERE CURRENT OF カーソル名を使うには、カーソル宣言に必ずFOR UPDATEが必要です。この場合、Oracleは内部的に現在行のROWIDで更新を行います。
  • 一意のキー指定WHERE CURRENT OFを使わない手動更新では、フェッチしたレコードの主キーやROWIDをWHERE句で必ず指定します。例えば、WHERE employee_id = rec.employee_idのようにしないと、意図しない複数行を更新する恐れがあります。ROWIDを取得しておき、WHERE ROWID = rec.rowidとする方法もあります。
  • LOCK対象テーブルの指定:複数テーブルをJOINしたSELECTの場合、FOR UPDATE OF カラムで指定したテーブルのみがロックされます。必要なテーブルだけをロック対象にすることができます。
  • コミットのタイミング:カーソル処理後は必ずCOMMITしてロックを解除します。コミットを忘れると行ロックが長時間残り、他セッションからの更新待機やデッドロックの原因になります。

以上のポイントを押さえれば、FOR UPDATE付きカーソルを使って安全に特定カラムを更新できます。

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?