2016-03-01 109 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