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.

これが私の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番目のプロシージャを実行できません。エラーメッセージ。ロックに問題があると思います。これを手伝ってください。

4

2 に答える 2

4

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

レコードは、トランザクションがコミットされるまで、最初の手順のためだけにロックされます。コミットは2番目のENDステートメントで発生します。

なぜそこにPAUSEがあるのか​​わかりません-トランザクションブロック内のIOをブロックしてはいけません-それは問題につながるだけです(ユーザーが起きてコーヒーを飲みに行く間にお互いをロックするなど...)

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

コードを書くためのより良い方法(両方のサンプルに同じ概念を適用する):

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.

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

于 2012-03-12T13:07:38.437 に答える
0

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

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

しかし、それは本当にマイナーです。私の意見でも重要なのは、トムがあなたのために彼の例で行ったことです。彼は、更新で使用したバッファー( "define buffer updCustomer for customer。")を定義しました。特に永続プロシージャまたはスーパープロシージャを使用する場合、または関数や内部プロシージャを使用する場合は、レコードを操作するたびにバッファを使用することをお勧めします。

なんで?バッファーを定義すると、更新するバッファーの範囲が、定義した場所に限定されます。例として、注意しないと、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」の上部に「DEFINEBUFFER...FOR MyTable」があり、「DEFINE BUFFER MyTAble for MyTable」であっても、この問題は解決されます(奇妙なことに...)。

そのため、DEFINE BUFFER ...を含むTomの例は、ABLで行う作業のテンプレートになるはずです。

この質問は以前に尋ねられました-https://stackoverflow.com/a/5490130/1433147を参照してください。

于 2012-06-12T04:33:29.227 に答える