助け合いフォーラム

Oracle DB

Oracle Master Silver SQL 2019(1Z0-071)
問題ID : 26743
問題を開く
次の順番でSQL文を実行した時の説明として、正しいものはどれですか(該当するものを全て選択して下さい)。
なお、それぞれのユーザは、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;

正解

①を実行すると、該当する行に排他ロックがかかる

②を実行すると、エラーが返される

③を実行すると、排他ロックが解除されるまで待機する

解説

設問のSQL文を1つずつ確認してみましょう。

 ユーザー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文の実行結果は次のようになります。


 

SQLを表示
ユーザーA:
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;

参考

通常、Oracle DatabaseではSELECT文の実行時はロックをかけませんが、SELECT文にFOR UPDATE句を指定することで、検索対象となる行に排他ロックをかけることができます。
FOR UPDATE句は次のように指定します。

 SELECT 列名 [,列名, ...]
 FROM 表名
 [WHERE 条件]
 FOR UPDATE [OF [表名.]列名] [NOWAIT | WAIT n]
 [ORDER BY 列名 [, 列名, ...]];


 

SQLを表示
ユーザーA:
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]


 
SQLを表示
ユーザーA:
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]


 
SQLを表示
ユーザーA:
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句に複数の表が指定されている場合は、それぞれの表の対象となる行にロックをかけますが、


 
SQLを表示
ユーザーA:
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 表名.列名オプションを指定すると、指定した表名.列名を含む行のみにロックを限定できます。


 
SQLを表示
ユーザーA:
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  について

公開日 2023/08/02

下記内容ですが上から順番に処理した場合に起こる内容が下記内容として解説されています。
ユーザー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がかけた排他ロックが解除されるまで待機します。

2023/08/03 11:55

ユーザー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


コメント

この返信に対して
コメントを記入できます

この投稿に対して返信しませんか?