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查询中的行,以确保没有其他线程写入我的更新。
答案 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 *;