为什么我们不能在事务块中继续我们想要的任何东西?
因为在 Oracle 中,readers don't block writers。如果您从表中select,您不会锁定它并且没有事务。所以,假设你这样做:
select col1, col2 into l_col1, l_col2
from table1
where ...;
if (<some logic based on l_col2`) then
update table1 set col2 = something where col1 = l_col1;
end if;
没关系;但是如果两个会话同时运行该代码,一个会话可能会更新 之间的行,而另一个会话选择并执行其更新 - 导致更新丢失或逻辑不正确,因为状态实际上并非如此正如预期的那样。
如果你改为select ... for update:
select col1, col2 into l_col1, l_col2
from table
where ...
for update; -- or: for update of col2
if (<some logic based on l_col2`) then
update table1 set col2 = something where col1 = l_col1;
end if;
那么当第一个会话select for update 时,表被锁定,因此第二个会话必须等待该事务完成才能获得锁,它会看到第一次更新应用之后行的状态。没有丢失更新,没有混乱。
您也可以锁定不想更新的行,正如the documentation 所说:
带有FOR UPDATE 子句的SELECT 语句(SELECT FOR UPDATE 语句)选择结果集中的行并锁定它们。 SELECT FOR UPDATE 允许您基于行中的现有值进行更新,因为它确保在您更新它们之前没有其他用户可以更改这些值。您还可以使用SELECT FOR UPDATE 锁定您不想更新的行,如示例 9-6 所示。
你可以调整行为:
默认情况下,SELECT FOR UPDATE 语句会一直等待,直到获得请求的行锁。要更改此行为,请使用SELECT FOR UPDATE 语句的NOWAIT、WAIT 或SKIP LOCKED 子句。有关这些子句的信息,请参阅 Oracle 数据库 SQL 语言参考。
如果select for update 在游标循环中,那么它也允许您这样做
update table1 set col2 = something where current of <cursor>;
当SELECT FOR UPDATE 与显式游标相关联时,该游标称为FOR UPDATE 游标。只有FOR UPDATE 游标可以出现在UPDATE 或DELETE 语句的CURRENT OF 子句中。 (CURRENT OF 子句是 SQL 语句 UPDATE 和 DELETE 的 WHERE 子句的 PL/SQL 扩展,将语句限制为游标的当前行。)