2011-07-20 129 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行,你會知道數據已經改變了,因爲你查詢了它並且你的應用程序需要採取適當的行動(即重新查詢數據,重新呈現給用戶,詢問用戶是否保留他們所做的任何未提交的更改等)。

但是,您的查詢確實有其他問題。聲明幾乎肯定不會做你認爲(或打算)做的事。此查詢從MYTABLE獲得任意10行,其中PROCS_DT爲NULL,然後對這些任意10行進行排序。

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

假設你真的想獲得「十佳」的結果,你需要做的ORDER BY在子查詢應用ROWNUM謂語前,即

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

或者你可以使用一個解析函數,即

  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進行樂觀鎖定似乎存在問題。請參閱http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:2680538100346782134#2680546500346035086 –

+0

+1指出OP的問題中的查詢已損壞。 –

+0

是的。感謝您在查詢中解決問題。作爲以前對我的堆棧溢出的答案的一部分q我實際上得到了你給我的確切查詢。但是從我的結尾來看,這是錯誤的錯誤。 – Shiv

3

要鎖定在Oracle中的行,你可以這樣做:

SELECT * 
    FROM table1 
WHERE some_condition 
FOR UPDATE OF table1; 

這是更好的,只是鎖定你需要的行,而不是整個表。

參考這裏: http://www.techonthenet.com/oracle/cursors/for_update.php

但在一般情況下,如果你用一個語句做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

謝謝。我認爲我的情況也類似於您的酒店預訂示例。我有2個node_index值爲0和1的節點,它們可以同時執行相同的更新查詢。在這種情況下,我希望不同的行集合被這些節點處理。如果我添加選擇更新在我的內部查詢它不工作。你的意思是我的更新陳述固有地處理了這種情況 – Shiv

+0

@Shiv - 我不認爲你需要在這裏使用SELECT FOR UPDATE,因爲你在單個操作中執行UPDATE。預留示例是查詢某些行的位置,然後經過一段時間,然後更新這些相同的行。沒有SELECT FOR UPDATE,那些行就可以改變。在你的情況下,你正在使用一個UPDATE語句,所以你的UPDATE語句將會看到你的語句開始執行時的行,除非你使用了一個不尋常的隔離級別。所以總而言之,我認爲一個UPDATE語句應該可以工作。 – dcp

+0

太好了。謝謝你.. – Shiv