我想一次性完成以下任务:
SELECT * FROM jobs WHERE status='PENDING';
UPDATE jobs SET status='RUNNING' WHERE status='PENDING';
因此,获取所有待处理的作业,然后立即将它们设置为“RUNNING”。
我不希望在两个语句中一个接一个地执行此操作的原因是作业可以在SELECT之后但在UPDATE之前作为'PENDING'添加到作业表中,因此我最终将作业设置为RUNNING即使在它处于PENDING状态时我还没有抓住它。
有没有在一个人这样做?所以我希望SELECT和UPDATE的结果能够即时发生。
感谢。
答案 0 :(得分:45)
为什么不使用RETURNING子句并在一个语句中处理这两件事:
UPDATE jobs
SET status='RUNNING'
WHERE status='PENDING'
RETURNING *
通过这种方式,您将获得UPDATE使用单个原子操作更改的所有行。
答案 1 :(得分:16)
通常,您应该使用一个UPDATE语句来执行此操作。 UPDATE通常不受UPDATE语句运行时可能已更改的行的影响,但是,最好读取事务隔离级别here。
假设您使用的是Read Committed的默认设置,这就是它所说的内容:
Read Committed是PostgreSQL中的默认隔离级别。当一个 事务在此隔离级别上运行,SELECT查询只能看到 在查询开始之前提交的数据;
关于UPDATE:
UPDATE,DELETE,SELECT FOR UPDATE和SELECT FOR SHARE命令 在搜索目标行方面表现与SELECT相同:它们 将只查找从命令start开始提交的目标行 时间。但是,这样的目标行可能已经更新(或 当它被另一个并发事务删除或锁定时 找到。在这种情况下,可能的更新程序将等待第一个 更新事务以提交或回滚(如果它仍在 进展)。如果第一个更新程序回滚,那么它的效果是 否定,第二个更新程序可以继续更新 最初找到了一排。如果第一个更新程序提交,则第二个更新程序 如果第一个更新程序删除了它将忽略该行,否则它将 尝试将其操作应用于行的更新版本。该 命令的搜索条件(WHERE子句)被重新评估为 查看该行的更新版本是否仍与搜索匹配 条件。如果是这样,第二个更新程序继续其操作, 从行的更新版本开始。 (在SELECT的情况下 FOR UPDATE和SELECT FOR SHARE,这意味着它是更新版本 锁定并返回给客户端的行。)
所以在你的场景中,一个UPDATE应该没问题。
请记住,有一个所谓的SELECT FOR UPDATE
语句,它将锁定您选择的行。你可以阅读here。
您需要使用此功能的方案将在预订系统中。考虑这个例子:
SELECT
以确定XYZ房间是否可用于日期X的预订。UPDATE
查询以预订房间。你看到这里的潜在问题了吗?如果在步骤1和2之间,房间被另一笔交易预订,那么当我们到达第2步时,我们将按照不再有效的假设进行操作,即房间可用。
但是,如果在步骤1中我们使用SELECT FOR UPDATE语句,我们确保没有其他事务可以锁定该行,所以当我们去更新行时,我们知道这样做是安全的。
但是,在你的场景中,不需要这个SELECT FOR UPDATE,因为你在一个语句中做了所有事情,并没有提前检查任何内容。
答案 2 :(得分:5)
begin;
select *
from jobs
where status='pending'
for update
;
update jobs
set status='running'
where status='pending';
commit;