2012-03-12 7 views
2

これは私の最初の手順です。ロックを使用中に更新値を取得していますか?

define frame LockFrame Customer.Name Customer.CreditLimit Customer.Balance. 
pause. 

DO TRANSACTION: 
    for each Customer exclusive-lock: 
    assign Customer.CreditLimit = Customer.CreditLimit + 5. 
    pause 1 no-message. 
    display Customer.Name Customer.CreditLimit Customer.Balance. 
    end. 
end. 

thsiは私の第2の手順です。

define frame LockFrame Customer.Name Customer.CreditLimit Customer.Balance. 
pause. 

DO TRANSACTION: 
    for each Customer exclusive-lock: 
    assign Customer.Balance= Customer.Balance + 2. 
    pause 1 no-message. 
    display Customer.Name Customer.CreditLimit Customer.Balance. 
    end. 
end. 

私は最初の手順を実行し、ちょうど2番目の手順の後、私は(CrditLimitここでは)最初で更新された値を取得する必要があります。(逆も同様) しかし、レコードがあるので、私は2番目に実行することはできませんよ最初にロックされています。エラーメッセージが表示されています。 私は問題が私のlocking.Pleaseこれで助けていると思う。

答えて

3

これはまさにあなたが期待するべきものです。

レコードは、トランザクションがコミットするまで、最初のプロシージャのためにのみロックされます。コミットは2番目のENDステートメントで行われます。

理由はわかりません。トランザクションブロック内でIOをブロックしてはいけません。問題が発生するだけです(ユーザーが起きてお互いにロックしてコーヒーを飲むなど)。 ..)

テーブル全体の更新をトランザクション(これはDO TRANSACTIONブロックの処理)で囲むことはほとんどありません。たとえそれがあなたがしたいことだと思っていたり、そうしなければならないと言われても、おそらく間違っています。これは、通常、「ビジネストランザクション」と「データベーストランザクション」を混同した結果です。つまり、大量のデータが再生されている場合は、同じではありません。

あなたのコードを書くためのより良い方法(両方のサンプルに同じ概念を適用):それぞれのためにNO-LOCKを使用していない

define buffer updCustomer for customer. 

for each customer no-lock /* where whatever */: 

    /* maybe some no-lock logic... */ 

    do for updCustomer transaction: 

    find updCustomer exclusive-lock where recid(updCustomer) = recid(customer). 
    assign 
     updCustomer.creditLimit = customer.creditLimit + 5. 
    . 

    display 
     updCustomer.name 
     updCustomer.creditLimit 
     updCustomer.balance 
    . 

    end. 

    pause 1 no-message. 

end. 

は選択ロジックまたは他のロジックがロックを必要とせずに実行することができます。更新バッファとDO FOR ... TRANSACTIONブロックを使用すると、レコードロックとトランザクションがその単一ブロックに厳密に適用されます。 PAUSEをブロックの外側に置くと、「ユーザーはコーヒーを飲む」という問題を防ぐことができます。

+0

私にとっては完璧です。ちょうど進歩を学び始めました。私はテーブル全体をロックするのではなく、1つのレコードをロックする方法を知らなかった。今私はそれを持っています。私はまた、あなたが提案したように実際に私が必要とする配置を一時停止する。貴重な明確な答えをありがとう。 –

0

私は彼が権威あると考えるので、トムが与えた答えにコメントするのをためらっています。しかし、私は2つの小さなことを指摘したい。

まず、ROWIDとしてRECIDを使用する方が良いかもしれません。 ROWIDはProgress(http://documentation.progress.com/output/OpenEdge102a/oe102ahtml/wwhelp/wwhimpl/common/html/wwhelp.htm?context=dvref&file=dvref-15-48.html参照)で使用することをお勧めします。これは、RECIDは「下位互換性のためにサポートされています」ということで、ほとんどのアプリケーションでは代わりにROWID関数を使用します。

しかし、それは本当にマイナーです。私の意見では、Tomが彼の事例でやったことも重要です。彼はアップデートで使用したバッファを定義しました( "顧客のためのバッファupdCustomerの定義")。レコードを扱うたびに、特に永続的なプロシージャやスーパープロシージャを使用したり、関数や内部プロシージャを使用している場合は、常にバッファを使用することをお勧めします。

なぜですか?バッファを定義すると、更新するバッファの範囲が定義した場所に限定されます。たとえば、Progressは、注意しないと、デフォルトのバッファをスーパープロシージャに「リーク」します。このシナリオを想像してみましょう。レコードを見つけ、スーパープロシージャ内の関数を呼び出して「何らかのもの」を実行してレコードを削除します。

FIND MyTable WHERE MyTable.fk = fkValue NO-LOCK NO-ERROR. 
UpdateOtherStuff(MyTable.fkValue). 
DeleteMyRecord(MyTable.fkValue). 

しかし、 "UpdateOtherStuff"では、これを含むいくつかの作業を行います...

FOR EACH MyTable: 
    If MyTable.Thing = 'ThingOne' THEN LEAVE. 
    /* other processing here... */ 
END. 

あなたはスーパーの手順は、デフォルトの「MyTableという」は、あなたのプログラムでバッファを共有し、あなたがしたくないどこかのレコードを再配置してしまうことを見つけたときあなたは驚くかもしれません...」とコールするようにDeleteMyRecord() "が期待どおりのレコードを持っていません。

"UpdateOtherStuff"に "DEFINE BUFFER ... MyTableのMyTable"があったとしても、上部に「DEFINE BUFFER ... FOR MyTable」があると問題は解決します。

DEFINE BUFFER ...を含むTomの例が、ABLで行う作業のテンプレートである必要があります。

この質問は以前の質問です。https://stackoverflow.com/a/5490130/1433147を参照してください。

関連する問題