我们有一个用于更新特定列的脚本。在这个脚本中,我们使用FOR UPDATE
游标。在脚本的第一个版本中,我们没有使用OF
子句的FOR UPDATE
部分。我们发现here和here这不应该影响脚本,因为所有连接表的所有行都应该被锁定,因此可以更新。
但是当我们运行脚本时,虽然打印了日志消息,但没有对列(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;
/
答案 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;