有没有办法通过SQL更新最新版本的行?

时间:2010-11-01 18:12:26

标签: oracle oracle10g oracle11g oracle9i

我读到Oracle维护行版本来处理并发。我想在非常大的实时数据库上运行更新查询,但此更新作业必须更改该行的最新版本。

这可以通过PL / SQL还是简单的SQL?

编辑如下**

让我清楚一下这个场景,我们在一个非常大的数据库上面临的现实问题。我们的客户是着名的手机服务提供商。

我们的数据库有一个表格,用于管理客户手机帐户中剩余的当前余额记录。在该表的其他列中,一列存储完成的充电量,另一列存储剩余的当前有效余额。

我们有两个独立的PL / SQL脚本。当客户为手机充电并更新其余额时,会自动触发一个脚本。

第二个脚本是关于从客户帐户中扣除某些费用。这是一个批处理作业,因为它适用于所有客户。此脚本计划在一天的特定时间间隔运行。运行此脚本时,它会在内存中加载50,000条记录,更新某些列并执行批量更新回表。

发生的问题是这样的:

一位身份证号为101的客户联系了他当地的商店,让他的手机充电。他付了这笔钱。但是直到他的手机即将充电,第二个脚本的预定时间才开始播放第二个脚本。第二个脚本在内存中加载了50,000个客户的记录。在这个内存记录中,也是该客户的记录之一。

直到第二个脚本的批量更新完成,第一个脚本才成功为客户的帐户充值。

现在发生的事情是实际的表,列:“CurrentAccountBalance”更新为150,但第二个脚本正在运行的内存记录有客户的旧余额,即100。

第二个脚本必须从列中扣除10:“CurrentAccountBalance”。根据实际工作情况,客户的“CurrentAccountBalance”应为140,这个问题使他的余额达到90.

现在该如何处理这个问题。

2 个答案:

答案 0 :(得分:4)

我认为如果你UPDATE,你想要的是无论发生什么。

Oracle确实会保留旧数据一段时间,但只是为了支持一致的读取。也就是说,读取操作只能看到事务开始时的状态 - 即使数据在此期间被覆盖。它被称为多版本并发控制,可以通过事务隔离级别进行控制。

您可以选择`FOR UPDATE来明确请求最新的一个;为记录添加锁定,以便其他人无法同时更新它(直到您的交易结束)。

但是,如果您需要编写任何内容(例如UPDATE),Oracle会在最新版本上始终

答案 1 :(得分:1)

正如@Markus所说,你有竞争条件。如果您在更新表中的行之前将记录加载到内存中并处理它们,而其他内容可能会在此期间尝试更新它们,那么您需要在处理它们时锁定它们。 (我假设你正在做的事情太复杂了,无法进行简单的一步更新)。像这样的东西会起作用:

DECLARE
    CURSOR c is SELECT * FROM current_balance_table FOR UPDATE;
BEGIN
    FOR r IN c LOOP
        /* Do whatever calculations you need */
        new_value := r.CurrantAccountBalance - 10;
        UPDATE current_balance_table SET CurrentAccountBalance = new_value
        WHERE CURRENT OF c;
    END LOOP:
END;

现在的问题是所有记录在循环期间都被锁定,因此商店中的客户要么无法更新余额,要么在更新生效之前等待日志 - 尽管它在它会对您存储的更新值起作用吗?因此,您必须将光标分成小块,平衡脚本的性能与尝试更新同一个表的其他任何人的影响。

一种选择是让外部游标选择您没有锁定的所有客户,然后选择一个内部游标,在计算和更新该行时锁定该客户的余额记录。您必须在每个内部循环之后提交以释放该行的锁定。这涉及到更多锁定/解锁和提交之后每行更新减慢了很多事情。但它最大限度地减少了对商店中个人客户的影响,因为一次只锁定一行,并且锁定的时间长度最小化。所以,你需要找到合适的平衡点。