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. ロックの影響(同時実行性への注意)
- 通常の
SELECT
(FOR 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付きカーソルを使って安全に特定カラムを更新できます。
参考資料