2016-03-01 66 views
1

我們有一個用於更新特定列的腳本。在這個腳本中,我們使用了一個FOR UPDATE遊標。在腳本的第一個版本中,我們沒有使用FOR UPDATE子句的OF部分。正如我們發現herehere這不應該影響腳本,因爲所有連接表的所有行都應該被鎖定,因此可以更新。Oracle FOR UPDATE(OF)遊標行爲

但是,當我們在運行腳本時雖然打印了日誌消息,但未對列(column_a)進行更新。

當我們在光標有FOR UPDATE OF t1.column_a的情況下更改腳本時,會出現相同的日誌消息,但更新是正確的!

任何人都可以解釋爲什麼沒有OF子句的腳本不起作用嗎?

Oracle數據庫版本爲'Oracle Database 11g企業版版本11.2.0.3.0',也使用'Oracle Database 12c企業版版本12.1.0.2.0 - 64位'進行了測試。

這裏是執行腳本的一個簡單的版本:

BEGIN 
     -- anonymous procedure 
     DECLARE PROCEDURE update_column_a IS 
     c_to_find CONSTANT NUMBER := -42; 
     c_constant_value CONSTANT VARCHAR2 := 'value'; 
     CURSOR c_my_cursor IS 
      SELECT t1.* 
      FROM table_1 t1, table_2 t2, table_3 t3 
      WHERE t1.t2_id = t2.id 
      AND t2.t3_id = t3.id 
      AND t3.column_b = c_to_find 
      -- FOR UPDATE with OF clause works 
      -- FOR UPDATE OF t1.column_a; 

      -- FOR UPDATE without OF clause does not 
      FOR UPDATE; 
     BEGIN 
     FOR cursor_rec IN c_my_cursor LOOP 
      IF cursor_rec.column_a IS NULL OR cursor_rec.column_a = '' THEN 
      dbms_output.put_line('Updating column...'); 
      UPDATE t1 SET column_a = c_constant_value WHERE CURRENT OF c_my_cursor; 
      ELSE 
      dbms_output.put_line('Column already set...'); 
      END IF; 
     END LOOP; 
     END update_column_a; 
     -- anonymous execution 
     BEGIN 
     update_column_a; 
     END; 
    END; 
    /

回答

1

據甲骨文11G PL/SQL文件here

當SELECT FOR UPDATE查詢多個表,它僅鎖定行 其列出現在FOR UPDATE子句中。

因此,在您的示例中可能看起來沒有行被鎖定,並且current of可能無法工作。

然而,當我試試這個:

declare 
    cursor c is 
    select ename, dname 
     from emp join dept on dept.deptno = emp.deptno 
     for update; 
begin 
    for r in c loop 
    null; 
    end loop; 
end; 

我發現,EMP和DEPT 的行鎖定(更新到無論是從另一個會話掛起)。

如果我更改代碼,試圖更新表之一,它工作正常EMP:

declare 
    cursor c is 
    select ename, dname 
     from emp join dept on dept.deptno = emp.deptno 
     for update; 
begin 
    for r in c loop 
    update emp 
     set ename = upper(ename) 
     where current of c; 
    end loop; 
end; 

但是,如果我嘗試更新DEPT,而不是我得到異常:

ORA-01410:無效的ROWID

這並不讓我感到吃驚,因爲我從EMP到DEPT的外鍵,和EMP將「鍵保存完好的」通過遊標的查詢,但DEPT w ^生病不是(即在結果中相同的DEPT行可能出現不止一次)。

這表明文檔是錯誤的,或者至少是誤導性的。然而,我看不出你的代碼如何不更新行,沒有像我一樣提出錯誤。

+0

正如我所瞭解的文檔,這減少了對具有指定列的表的鎖定。根據該[網站](http://www.fast-track.cc/___aaa_pl_sql_cursor_for_update.htm)應該鎖定所有行。 '如果遊標將連接表,則兩個表中游標返回的所有行都被鎖定。' – sebastian

+0

我們沒有得到任何異常。這也讓我們感到困惑,因爲它不會更新列。 – sebastian

+0

我不會相信任何Don Burleson通常會發布的東西,但他似乎就在那裏! –

0

「任何人都可以解釋爲什麼沒有OF條款腳本不起作用?「

你幾乎沒有:)

什麼的FOR定期更新? - >鎖定結果集

  SELECT t1.* 
      FROM table_1 t1, table_2 t2, table_3 t3 
      WHERE t1.t2_id = t2.id 
      AND t2.t3_id = t3.id 
      AND t3.column_b = c_to_find 
      FOR UPDATE; 

所以,這樣你不能在UPDATE沒有這些表結果集。

但是,如果你指定條款的FOR UPDATE,然後你告訴給ORACLE你想使鎖異常,命名特定列。

 SELECT t1.* 
     FROM table_1 t1, table_2 t2, table_3 t3 
     WHERE t1.t2_id = t2.id 
     AND t2.t3_id = t3.id 
     AND t3.column_b = c_to_find 
     FOR UPDATE OF t1.column_a; 
+0

我不這麼認爲。據我瞭解的文件OF子句是用來有一個更精確的鎖應該更高性能。 FOR UPDATE沒有OF應該也能工作。 – sebastian

0

我在其中一本書中遇到了下面幾行。

您可以在SELECT中針對多個表使用FOR UPDATE子句。在這種情況下,只有在FOR UPDATE子句引用該表中的列時,才鎖定表中的行。在以下示例中,FOR UPDATE子句不會導致冬化表中的任何鎖定行:

CURSOR fall_jobs_cur IS 
SELECT w.task, w.expected_hours, 
     w.tools_required, 
     w.do_it_yourself_flag 
    FROM winterize w, husband_config hc 
    WHERE w.year_of_task = TO_CHAR (SYSDATE, 'YYYY') 
    AND w.task_id = hc.task_id 
    FOR UPDATE OF hc.max_procrastination_allowed; 
+0

我們已經闡明。不帶OF子句的FOR UPDATE鎖定FROM子句中的任何表。如果你想測試它,你可能已經看到了它...... – sebastian