助け合いフォーラム
なお、それぞれのユーザは、Oracleへの接続には同一アカウント(pingt)を使用して同じデータにアクセスしているものとします。
ユーザーA:
SQL> SELECT prodid, name, category FROM prod WHERE prodid = 2
2 FOR UPDATE WAIT 10; … ①
ユーザーB:
SQL> SELECT * FROM prod WHERE prodid = 2
2 FOR UPDATE NOWAIT; … ②
SQL> UPDATE prod SET name = 'Chopin' WHERE prodid = 2; … ③
ユーザーA:
SQL> COMMIT;
ユーザーB:
SQL> COMMIT;
正解
①を実行すると、該当する行に排他ロックがかかる
②を実行すると、エラーが返される
③を実行すると、排他ロックが解除されるまで待機する
解説
ユーザーA:
SQL> SELECT prodid, name, category FROM prod WHERE prodid = 2
2 FOR UPDATE WAIT 10; … ①
ユーザーB:
SQL> SELECT * FROM prod WHERE prodid = 2
2 FOR UPDATE NOWAIT; … ②
SQL> UPDATE prod SET name = 'Chopin' WHERE prodid = 2; … ③
ユーザーA:
SQL> COMMIT;
ユーザーB:
SQL> COMMIT;
①:SELECT文にFOR UPDATE句が指定されているので、SELECT文で取り出される行に排他ロックがかけられます。
②:①でユーザーAが排他ロックをかけているため、ユーザーBはSELECT文にFOR UPDATE句を指定しても排他ロックをかけることはできません。さらにNOWAITオプションを指定しているので、SELECT文実行後、直ちにエラーとなります。
③:①でユーザーAが排他ロックをかけているため、ユーザーBがUPDATE文実行時、ユーザーAがかけた排他ロックが解除されるまで待機します。
以上より、
・①を実行すると、該当する行に排他ロックがかかる
・②を実行すると、エラーが返される
・③を実行すると、排他ロックが解除されるまで待機する
が正解となります。
設問のSQL文の実行結果は次のようになります。
SELECT prodid, name, category FROM prod WHERE prodid = 2
FOR UPDATE WAIT 10;
ユーザーB:
SELECT * FROM prod WHERE prodid = 2
FOR UPDATE NOWAIT;
UPDATE prod SET name = 'Chopin' WHERE prodid = 2;
ユーザーA:
COMMIT;
ユーザーB:
COMMIT;
参考
FOR UPDATE句は次のように指定します。
SELECT 列名 [,列名, ...]
FROM 表名
[WHERE 条件]
FOR UPDATE [OF [表名.]列名] [NOWAIT | WAIT n]
[ORDER BY 列名 [, 列名, ...]];
SELECT employee_id, employee_name, hiredate
FROM employees
WHERE department_id IN (1, 2, 3)
FOR UPDATE;
ユーザーB:
UPDATE employees SET department_id = 5
WHERE hiredate >= '10-04-01';
ユーザーA:
ROLLBACK;
FOR UPDATE句では、次のオプションを指定できます。
[NOWAIT]
SELECT employee_id, employee_name, hiredate
FROM employees
FOR UPDATE;
ユーザーB:
SELECT employee_id, employee_name, hiredate
FROM employees
WHERE department_id IN (1, 2, 3)
FOR UPDATE NOWAIT;
[WAIT n]
SELECT employee_id, employee_name, hiredate
FROM employees
FOR UPDATE;
ユーザーB:
SELECT employee_id, employee_name, hiredate
FROM employees
FOR UPDATE WAIT 5;
[OF 表名.列名]
FROM句に複数の表が指定されている場合は、それぞれの表の対象となる行にロックをかけますが、
SELECT department_name, employee_name, hiredate
FROM departments JOIN employees
USING(department_id)
FOR UPDATE;
ユーザーB:
SELECT department_id, department_name
FROM departments
FOR UPDATE WAIT 3;
SELECT employee_id, employee_name, hiredate
FROM employees
FOR UPDATE WAIT 5;
OF 表名.列名オプションを指定すると、指定した表名.列名を含む行のみにロックを限定できます。
SELECT department_name, employee_name, hiredate
FROM departments JOIN employees
USING(department_id)
FOR UPDATE OF departments.department_name;
ユーザーB:
SELECT department_id, department_name
FROM departments
FOR UPDATE WAIT 5;
SELECT employee_id, employee_name, hiredate
FROM employees
FOR UPDATE WAIT 5;
FOR UPDATE WAIT について
下記内容ですが上から順番に処理した場合に起こる内容が下記内容として解説されています。
ユーザーAが発行したFOR UPDATE WAIT 10はユーザーAの前の処理に対して働いていると考えますがあっているでしょうか?
また、解説③にはユーザーAがかけた排他ロックが解除されるまで待機するとありますが、これはユーザーBのSQL文でUPDATE prod SET name = 'Chopin' WHERE prodid = 2 の後に FOR UPDATE WAIT がありませんがデフォルトでそのような挙動になるのでしょうか?
お手数おかけしますがよろしければご教授お願いします。
ユーザーA:
SQL> SELECT prodid, name, category FROM prod WHERE prodid = 2
2 FOR UPDATE WAIT 10; … ①
ユーザーB:
SQL> SELECT * FROM prod WHERE prodid = 2
2 FOR UPDATE NOWAIT; … ②
SQL> UPDATE prod SET name = 'Chopin' WHERE prodid = 2; … ③
ユーザーA:
SQL> COMMIT;
ユーザーB:
SQL> COMMIT;
①:SELECT文にFOR UPDATE句が指定されているので、SELECT文で取り出される行に排他ロックがかけられます。
②:①でユーザーAが排他ロックをかけているため、ユーザーBはSELECT文にFOR UPDATE句を指定しても排他ロックをかけることはできません。さらにNOWAITオプションを指定しているので、SELECT文実行後、直ちにエラーとなります。
③:①でユーザーAが排他ロックをかけているため、ユーザーBがUPDATE文実行時、ユーザーAがかけた排他ロックが解除されるまで待機します。
ユーザーAが発行したFOR UPDATE WAIT 10はユーザーAの前の処理に対して働いていると考えますがあっているでしょうか?
そうですね。
①のSELECT文で「FOR UPDATE」を用いてロックを取得しようとしていますが、競合するトランザクションがすでにある場合には待たされます。「WAIT 10」は競合するトランザクションがすでにあった場合に「10秒までは待つ」という意味で、10秒経過するとエラーが返ります。
この設問では、①の前に何かトランザクションがあるか否かといった情報は特に提示されていないので、ロックを取得できると考えられます。
また、解説③にはユーザーAがかけた排他ロックが解除されるまで待機するとありますが、これはユーザーBのSQL文でUPDATE prod SET name = 'Chopin' WHERE prodid = 2 の後に FOR UPDATE WAIT がありませんがデフォルトでそのような挙動になるのでしょうか?
FOR UPDATE句はSELECT文で使用できる句です。UPDATE文ではそもそもロックを取得するので、FOR UPDATE句はありません。(「SELECT ... FOR UPDATE」文としてひと塊りで覚えてしまった方がわかりやすいかもしれません)
参考: DML操作での自動ロック
https://docs.oracle.com/cd/F19136_01/sqlrf/Automatic-Locks-in-DML-Operations.html#GUID-3D57596F-8B73-4C80-8F4D-79A12F781EFD
コメント
この投稿に対して返信しませんか?