使用select和row lock更新Query

时间:2011-07-20 16:43:55

标签: sql oracle locking oracle11g

update mytable set node_index=0 where id in (
        SELECT 
            id
         FROM mytable
         WHERE 
            rownum<=10 and PROCS_DT is null  
         order by CRET_DT,PRTY desc) 

我将此查询作为上一个问题答案的一部分。现在如何锁定select查询中的行,以确保没有其他线程写入我的更新。

3 个答案:

答案 0 :(得分:5)

我不清楚你是否认为自己有这个问题。

更新表中的行本身会在该行上放置行级锁。在您通过提交或回滚结束事务来释放锁之前,没有其他会话可以更新该行。完成交易后,其他会话可以自由覆盖您的更新。锁定SELECT中的行没有必要或有益。

如果您正在尝试编写应用程序以避免丢失更新(即,您希望避免另一个会话更新您提交事务后所执行的相同行而不向其他用户显示您的更新数据),您的应用程序将需要实现某种额外的锁定。最有效的方法是使用表中的某种LAST_UPDATE_TIMESTAMP列实现乐观锁定。假设您已将此列添加到表中(如果它还没有),则只要您查询要向用户显示的数据,就会选择LAST_UPDATE_TIMESTAMP,并在{{1}中指定LAST_UPDATE_TIMESTAMP }}。如果您的UPDATE正好更新了1行,则您知道自查询以来没有人更改过数据。如果您的UPDATE更新了0行,您就会知道数据已经更改,因为您查询了它并且您的应用程序需要采取适当的操作(即重新查询数据,将其重新呈现给用户,询问用户是否保留他们所做的任何未提交的更改等。)。

但是,您的查询确实存在其他问题。 UPDATE语句几乎肯定不会按照您的想法(或打算)来执行。此查询从SELECT获取任意10行,其中MYTABLE为NULL,然后对这些任意10行进行排序。

PROCS_DT

假设您确实希望获得“前10名”结果,则需要在应用 SELECT id FROM mytable WHERE rownum<=10 and PROCS_DT is null order by CRET_DT,PRTY desc 谓词之前在子查询中执行ORDER BY,即

ROWNUM

或者您可以使用分析函数,即

         SELECT 
            id
         FROM (SELECT *
                 FROM mytable
                WHERE procs_dt IS NULL
                ORDER BY cret_dt, prty desc)
         WHERE 
            rownum<=10 

答案 1 :(得分:3)

要锁定Oracle中的行,您可以执行以下操作:

SELECT *
  FROM table1
WHERE some_condition
FOR UPDATE OF table1;

最好只锁定所需的行而不是整个表。

参考这里: http://www.techonthenet.com/oracle/cursors/for_update.php

但一般情况下,如果您使用单个语句执行UPDATE,则不必担心锁定表甚至行。当你必须使用锁定行所需的多个语句时。

您需要使用此功能的方案是在预订系统中。考虑这个例子:

1. Execute SELECT to find out if room XYZ is available for a reservation on date X.
2. The room is available. Execute UPDATE query to book the room.

你看到这里的潜在问题了吗?如果在步骤1和2之间,房间被另一个交易预订,那么当我们到达第2步时,我们的假设不再有效,即房间可用。

但是,如果在步骤1中我们使用SELECT FOR UPDATE语句,我们确保没有其他事务可以锁定该行,所以当我们去更新行时,我们知道这样做是安全的。

答案 2 :(得分:0)

您可以尝试在嵌套查询中使用 SKIP LOCKED

有关详细信息,请查看此博客:

https://www.2ndquadrant.com/en/blog/what-is-select-skip-locked-for-in-postgresql-9-5/

不是一个确切的例子,但是您可以从这个例子中得到启发:

update mytable set node_index=0 where id in (
        SELECT 
         id
        FROM mytable
        WHERE 
            rownum<=10 and PROCS_DT is null  
        order by CRET_DT,PRTY desc
        FOR UPDATE SKIP LOCKED)
 RETURNING *;