Oracle FOR UPDATE(OF)游标行为

时间:2016-03-01 12:23:59

标签: sql oracle plsql sql-update cursor

我们有一个用于更新特定列的脚本。在这个脚本中,我们使用FOR UPDATE游标。在脚本的第一个版本中,我们没有使用OF子句的FOR UPDATE部分。我们发现herehere这不应该影响脚本,因为所有连接表的所有行都应该被锁定,因此可以更新。

但是当我们运行脚本时,虽然打印了日志消息,但没有对列(column_a)进行更新。

当我们使用带有FOR UPDATE OF t1.column_a的光标更改脚本时,会显示相同的日志消息,但更新是正确的!

任何人都可以解释为什么没有OF子句脚本不起作用吗?

Oracle数据库版本是“Oracle数据库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;
    /

3 个答案:

答案 0 :(得分:1)

根据Oracle 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不会(即同一个DEPT行在结果中可以出现多次)。

这告诉我文档是错误的,或者至少是误导性的。但是,我无法看到你的代码如何不更新行,而不像我的那样引发错误。

答案 1 :(得分:0)

"任何人都可以解释为什么没有OFclause脚本不起作用?"

你快到了:)

常规的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;

因此,您不能在结果集中更新这些表。

但是,如果你指定FOR UPDATE OF子句,那么你要告诉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;

答案 2 :(得分:0)

我在其中一本书中遇到过以下几行。

您可以在SELECT中对多个表使用FOR UPDATE子句。在这种情况下,仅当FOR UPDATE子句引用该表中的列时,才会锁定表中的行。在以下示例中,FOR UPDATE子句不会导致winterize表中的任何锁定行:

 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;