Oracle FOR UPDATE (OF) 游标行为
Oracle FOR UPDATE (OF) Cursor behaviour
我们有一个用于更新特定列的脚本。在此脚本中,我们使用 FOR UPDATE
游标。在脚本的第一个版本中,我们没有使用 FOR UPDATE
子句的 OF
部分。我们发现 here and here 这不应该影响脚本,因为所有连接表的所有行都应该被锁定,因此可以更新。
但是当我们是 运行 脚本时,尽管打印了日志消息,但未对列进行更新 (column_a)。
当我们使用具有 FOR UPDATE OF t1.column_a
的光标更改脚本时,会出现相同的日志消息,但更新是正确的!
谁能解释为什么脚本在没有 OF
子句的情况下无法运行?
Oracle 数据库版本是 'Oracle Database 11g Enterprise Edition Release 11.2.0.3.0' 也用 'Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit' 测试过。
这是已执行脚本的简单版本:
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;
/
根据 Oracle 11G PL/SQL 文档 here:
When SELECT FOR UPDATE queries multiple tables, it locks only rows
whose columns appear in the FOR UPDATE clause.
因此,在您的示例中,似乎没有任何行被锁定,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: invalid ROWID
这并不让我吃惊,因为我有一个从 EMP 到 DEPT 的外键,并且 EMP 将被游标查询"key-preserved",但 DEPT 不会(即可以出现相同的 DEPT 行)结果中不止一次)。
这向我表明文档是错误的,或者至少是误导性的。但是,我看不出你的代码怎么能不更新行,而不会像我的那样引发错误。
"Can anyone explain why the script does not work without the OFclause?"
你快到了:)
常规的FOR UPDATE是做什么用的? --> 锁定结果集
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;
因此,这样您就无法更新结果集中的这些表 none。
但是,如果您指定了 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;
我在其中一本书中看到以下几行。
您可以在 SELECT 中针对多个 table 使用 FOR UPDATE 子句。在这种情况下,仅当 FOR UPDATE 子句引用 table 中的列时,table 中的行才被锁定。在以下示例中,FOR UPDATE 子句不会导致 winterize table:
中的任何锁定行
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;
我们有一个用于更新特定列的脚本。在此脚本中,我们使用 FOR UPDATE
游标。在脚本的第一个版本中,我们没有使用 FOR UPDATE
子句的 OF
部分。我们发现 here and here 这不应该影响脚本,因为所有连接表的所有行都应该被锁定,因此可以更新。
但是当我们是 运行 脚本时,尽管打印了日志消息,但未对列进行更新 (column_a)。
当我们使用具有 FOR UPDATE OF t1.column_a
的光标更改脚本时,会出现相同的日志消息,但更新是正确的!
谁能解释为什么脚本在没有 OF
子句的情况下无法运行?
Oracle 数据库版本是 'Oracle Database 11g Enterprise Edition Release 11.2.0.3.0' 也用 'Oracle Database 12c Enterprise Edition Release 12.1.0.2.0 - 64bit' 测试过。
这是已执行脚本的简单版本:
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;
/
根据 Oracle 11G PL/SQL 文档 here:
When SELECT FOR UPDATE queries multiple tables, it locks only rows whose columns appear in the FOR UPDATE clause.
因此,在您的示例中,似乎没有任何行被锁定,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: invalid ROWID
这并不让我吃惊,因为我有一个从 EMP 到 DEPT 的外键,并且 EMP 将被游标查询"key-preserved",但 DEPT 不会(即可以出现相同的 DEPT 行)结果中不止一次)。
这向我表明文档是错误的,或者至少是误导性的。但是,我看不出你的代码怎么能不更新行,而不会像我的那样引发错误。
"Can anyone explain why the script does not work without the OFclause?"
你快到了:)
常规的FOR UPDATE是做什么用的? --> 锁定结果集
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;
因此,这样您就无法更新结果集中的这些表 none。
但是,如果您指定了 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;
我在其中一本书中看到以下几行。
您可以在 SELECT 中针对多个 table 使用 FOR UPDATE 子句。在这种情况下,仅当 FOR UPDATE 子句引用 table 中的列时,table 中的行才被锁定。在以下示例中,FOR UPDATE 子句不会导致 winterize table:
中的任何锁定行 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;