2011-07-20 131 views
2
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查询中的行以确保没有其他线程写入我的更新。更新查询与选择和行锁

回答

5

我不清楚你有什么问题,你似乎认为你有。

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

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

但是,您的查询确实有其他问题。声明几乎肯定不会做你认为(或打算)做的事。此查询从MYTABLE获得任意10行,其中PROCS_DT为NULL,然后对这些任意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 

或者你可以使用一个解析函数,即

  SELECT 
      id 
     FROM (SELECT m.*, 
         rank() over (ORDER BY cret_dt, prty desc) rnk 
       FROM mytable m 
       WHERE procs_dt IS NULL) 
     WHERE 
      rnk<=10 
+2

“最有效的方法是使用ORA_ROWSCN伪列实现乐观锁定。“使用ORA_ROWSCN进行乐观锁定似乎存在问题。请参阅http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:2680538100346782134#2680546500346035086 –

+0

+1指出OP的问题中的查询已损坏。 –

+0

是的。感谢您在查询中解决问题。作为以前对我的堆栈溢出的答案的一部分q我实际上得到了你给我的确切查询。但是从我的结尾来看,这是错误的错误。 – Shiv

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语句,我们确保没有其他事务可以锁定该行,所以当我们去更新行时,我们知道这样做是安全的。

+0

谢谢。我认为我的情况也类似于您的酒店预订示例。我有2个node_index值为0和1的节点,它们可以同时执行相同的更新查询。在这种情况下,我希望不同的行集合被这些节点处理。如果我添加选择更新在我的内部查询它不工作。你的意思是我的更新陈述固有地处理了这种情况 – Shiv

+0

@Shiv - 我不认为你需要在这里使用SELECT FOR UPDATE,因为你在单个操作中执行UPDATE。预留示例是查询某些行的位置,然后经过一段时间,然后更新这些相同的行。没有SELECT FOR UPDATE,那些行就可以改变。在你的情况下,你正在使用一个UPDATE语句,所以你的UPDATE语句将会看到你的语句开始执行时的行,除非你使用了一个不寻常的隔离级别。所以总而言之,我认为一个UPDATE语句应该可以工作。 – dcp

+0

太好了。谢谢你.. – Shiv