2011-07-20 17 views
2
update mytable set node_index=0 where id in (
     SELECT 
      id 
     FROM mytable 
     WHERE 
      rownum<=10 and PROCS_DT is null 
     order by CRET_DT,PRTY desc) 

私はこの質問を前回の質問の回答として受け取りました。次に、selectクエリの行をロックして、他のスレッドが自分の更新を上書きしないようにする方法。選択と行ロックでクエリを更新

答えて

5

あなたには、あなたが持っていると思われる問題があることは明らかです。

テーブルの行を更新すると、本来、その行に行レベルのロックが設定されます。コミットまたはロールバックによってトランザクションを終了してロックを解除するまで、他のセッションはその行を更新できません。トランザクションを完了すると、他のセッションで更新プログラムを上書きすることができます。 SELECTの行をロックする必要はありません。

更新が失われないようにアプリケーションを作成しようとしている場合(つまり、更新されたデータを最初に他のユーザーに表示せずにトランザクションをコミットした後に行ったのと同じ行を別のセッションで更新しないようにしたい場合)何らかの追加のロックを実装する必要があります。最も効率的なのは、表のなんらかのLAST_UPDATE_TIMESTAMP列を使用してオプティミスティック・ロックを実装することです。テーブルにこの列を追加していない場合は、ユーザーに提示するデータを照会するたびにLAST_UPDATE_TIMESTAMPを選択し、UPDATEにはLAST_UPDATE_TIMESTAMPを指定します。 UPDATEが正確に1行更新された場合、クエリを実行してから誰もデータを変更していないことがわかります。 UPDATEが0行更新された場合、クエリを実行してからデータが変更されたことがわかり、アプリケーションは適切な処置(つまり、データの再クエリ、ユーザーへの再提示、彼らが行ったコミットされていない変更のいずれかを維持するなど)。

クエリには別の問題があります。 SELECTのステートメントは、あなたが思うような(または意図する)ことはほとんどありません。このクエリは、MYTABLEから任意の10行を取得します。ここで、PROCS_DTはNULLで、これらの任意の10行を並べ替えます。

  SELECT 
      id 
     FROM mytable 
     WHERE 
      rownum<=10 and PROCS_DT is null 
     order by CRET_DT,PRTY desc 

あなたが実際に「トップ10」の結果を取得したいと仮定すると、あなたはすなわち、ROWNUM述語を適用する前に、サブクエリで

  SELECT 
      id 
     FROM (SELECT * 
       FROM mytable 
       WHERE procs_dt IS NULL 
       ORDER BY cret_dt, prty desc) 
     WHERE 
      rownum<=10 

ORDER BYを行う必要がありますまたはあなたが使用することができますすなわち、

  SELECT 
      id 
     FROM (SELECT m.*, 
         rank() over (ORDER BY cret_dt, prty desc) rnk 
       FROM mytable m 
       WHERE procs_dt IS NULL) 
     WHERE 
      rnk<=10 
+2

"最もORA_ROWSCN疑似列を使用してオプティミスティック・ロックを実装することが効率的です。オプティミスティック・ロックのためにORA_ROWSCNを使用することには問題があるようです。 OPの質問のクエリが壊れていることを指摘するため、http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:2680538100346782134#2680546500346035086 –

+0

+1を参照してください。 –

+0

真。私の質問で問題を指摘していただきありがとうございます。私のスタックオーバーフローに対する以前の答えの一部として、私はあなたが私に与えた正確なクエリを実際に得ました。しかし、それは私の最後からタイプミスです。 – Shiv

3

は、Oracleの行をロックするには、あなたが行うことができます:

SELECT * 
    FROM table1 
WHERE some_condition 
FOR UPDATE OF table1; 

それはちょうどあなたが必要な行の代わりに、テーブル全体をロックするためにはるかに良いです。

はここに参照してください: http://www.techonthenet.com/oracle/cursors/for_update.php

をしかし、あなたは1つのステートメントでUPDATEを実行している場合、一般的に、あなたは、テーブルまたは偶数行のロックを心配する必要はありません。行をロックするために必要な複数のステートメントを使用する必要があるときです。

この機能を使用する必要があるシナリオは予約システムにあります。この例を考えてみましょう:

1. Execute SELECT to find out if room XYZ is available for a reservation on date X. 
2. The room is available. Execute UPDATE query to book the room. 

ここに潜在する問題がありますか?ステップ1とステップ2の間に部屋が別の取引によって予約されると、ステップ2に達すると、もはや有効ではない、つまり部屋が利用可能であるという前提で操作しています。

ただし、手順1でSELECT FOR UPDATE文を使用すると、他のトランザクションでその行をロックできないことが保証されているので、行を更新するときは安全です。

+0

ありがとうございました。私は私の場合もurホテル予約の例に似ていると思う。私は0と1のnode_index値を持つ2つのノードを持っており、同じ更新クエリを同時に実行することができます。その場合、私はそれらのノードによって処理される異なる行のセットを必要とします。私の内部クエリで更新のための選択を追加する場合、それは動作していません。私の更新ステートメントが本質的にこのシナリオを処理することを意味しますか? – Shiv

+0

@ Shiv - 一回の操作でUPDATEを行っているので、SELECT FOR UPDATEを使用する必要はありません。予約の例では、いくつかの行を照会し、ある程度の時間が経過した後、同じ行を更新します。 SELECT FOR UPDATEがないと、これらの行が変更される可能性があります。あなたのケースでは、単一のUPDATEステートメントを使用しているので、UPDATEステートメントは、珍しい分離レベルを使用していない限り、ステートメントの実行が開始されたときの行を表示します。要約すると、私は1つのUPDATE文がうまくいくと思います。 – dcp

+0

ありがとうございます.. – Shiv