- PR -

oracle レコードロックについて

投稿者投稿内容
ビギ
ベテラン
会議室デビュー日: 2006/04/03
投稿数: 56
投稿日時: 2008-05-22 09:51
oracle9iを使用しています。
SQL文に詳しくないのと、今まであまり明示的にロックを意識して使ったことがなく不安なので教えてください。

やりたいことは、
処理としては、KEYがvarchar2型の・・・例えば、日時分(YYYYMMDDhhmm)というようなものとします。
日時分の範囲指定で、その範囲のレコードが存在した場合、データをUPDATEし、存在しないレコードの場合はINSERTしなければなりません。
1レコードでもUPDATE,INSERTに失敗したらRollbackし、すべて成功したらCommitします。
こういう場合、select 〜 for update を使った方がいいのでしょうか。
lock tableで、Keyを「1」ずつ増やして、selectしてfetchできたらupdate、fetchエラーならinsertとやってみたのですが、nowaitは使えないし、selectの回数が多いのでかなりパフォーマンスが低下する・・・と言われました。

select * from テーブル where Keyが開始〜終了の範囲 for update;
 for文・・・ 開始Keyから終了Keyまで「+1」していく(該当Key)
fetchされたKeyをチェックし、
    該当Keyより大きかったら、該当Keyまでinsert、
    該当Keyと同じだったら、データを更新してupdate
すべて正常に終了したらcommitする。

というような方法に変更しようと思ってるのですが、このような方法でいいのでしょうか。
アドバイスをいただけないでしょうか。
よっしー
大ベテラン
会議室デビュー日: 2007/05/17
投稿数: 143
投稿日時: 2008-05-24 00:46
ビギさん、こんにちは。

MERGEは使えませんか?
・・・実は使ったことないですが。
ビギ
ベテラン
会議室デビュー日: 2006/04/03
投稿数: 56
投稿日時: 2008-05-24 14:19
よっしーさん、ありがとうございます!
MERGE というのがあるんですね、知りませんでした。

例文をネットで検索してマネしながらSQL文を作っていますが
なかなか難しいもんですね。でも なんとか頑張ります。

だだ、この1文に
 for update
のような排他はかけられるんでしょうか?


よっしー
大ベテラン
会議室デビュー日: 2007/05/17
投稿数: 143
投稿日時: 2008-05-24 18:05
ビギさん
引用:

だだ、この1文に
 for update
のような排他はかけられるんでしょうか?


INSERTやUPDATEにも「for update」ってつけますか?
unibon
ぬし
会議室デビュー日: 2002/08/22
投稿数: 1532
お住まい・勤務地: 美人谷        良回答(20pt)
投稿日時: 2008-05-24 18:28
引用:

ビギさんの書き込み (2008-05-22 09:51) より:
lock tableで、Keyを「1」ずつ増やして、selectしてfetchできたらupdate、fetchエラーならinsertとやってみたのですが、nowaitは使えないし、selectの回数が多いのでかなりパフォーマンスが低下する・・・と言われました。


「と言われました」とは誰に言われたのかが気になるところですが。

要求仕様が良く分かりませんが、普通にトランザクションを開始して、SELECT/UPDATE/INSERT すれば良いだけであり、必要に応じて FOR UPDATE を付けるだけだろうと思います。
おっしゃる「Key」の使い方がとくに良く分かりませんでした。「lock table」はなんのために要るのでしょうか?なにか連番発行用のテーブルを別途持っているのでしょうか?「Key」を既存のレコードから引っ張ってきているのなら集合演算でかなり簡素な SQL が書けそうな気もしますが、「Key」を恣意的に生成しているようだと、ロジックが複雑になるのは避けられないような気もします。
ビギ
ベテラン
会議室デビュー日: 2006/04/03
投稿数: 56
投稿日時: 2008-05-25 10:19
よっしーさん、unibonさん、ありがとうございます。

仕様を細かくかけないのですが、おおまかに書きますと、
1分単位に使えるMAXの「数」というのがありまして、
20人で、使用する「数」を予約できるという仕様です。
数は1からMAXまで自由です。(空いてる数だけ予約可能)

予約は時間の範囲指定で行います。(最低1分)
MAXの数が決まっているので、予約しようとしている時間帯の中で1分でもその数をオーバーすると、その予約はできないことになります。
なので、予約しようとしている時間範囲については、排他をかけないとまずいのでは・・・と思ったしだいです。

最初に、範囲指定した時間帯の中で「数」が1分でもオーバーしていないかをチェックし、
オーバーしていたら「予約できません」のエラー、
オーバーしていなかったら、
そのテーブル(または予約時間帯全部のレコード)に排他をかけて
予約時間帯の数を1分単位で更新、
  すでに予約されていたら、「数」を加算してupdate
新規の予約だったら「数」をそのままinsert
※予約できる時間帯は一日だけとかではなく、無期限です。

予約しようとする全時間帯を排他をかけて、更新しないといけないのでlock tableを使ったのですが
その間他プロセスは待たされるみたいなので
  for update を使おうと思いました。 


select 〜 for update
 で1件ずつ、読み込んで
   updateかinsertかをチェックして更新/登録
・・・でいけそうでしょうか。
排他を使ったことがないので、insertをした時は排他ってどうなるんだろう???
って思ったしだいです。

ビギ
ベテラン
会議室デビュー日: 2006/04/03
投稿数: 56
投稿日時: 2008-05-27 18:14
使い方が間違っているのでしょうか。
行き詰ってしまいました。

select 〜 from AAA where 日時 between 開始 and 終了 for update nowait;
で、
1. fetch あり --> update
2. 次のfetch --> 次のデータをfetchできず、insert --> error!

実際には5件ほどfetchできるテストデータがあるため、
insertでエラーになってしまいました。

betweenの間の updateとinsertを実行しないでSQLのログだけ書いてみたら、
updateの行、insertの行は正しくログに書かれていました。
つまりは、update、insertを行うとfetchの結果がクリアされてしまいます。
(これは当たり前ですよね。)
排他をかけているので、別のオブジェクトを使うこともできません。

こういう場合、
select〜 between 〜 for update
の中でupdate insertを複数行(というか、betweenの間のすべての行)、
行う事はできないのでしょうか?

何かいい方法がないか、アドバイスお願いいたします。

 
  
ほったて
ベテラン
会議室デビュー日: 2007/11/10
投稿数: 68
投稿日時: 2008-05-27 18:55
何からトランザクションを実行しているかとエラーが不明ですが、自動コミットが有効になってるからselect ... for updateのカーソルがクローズされただけでは。

スキルアップ/キャリアアップ(JOB@IT)