【问题标题】:postgres update after select选择后postgres更新
【发布时间】:2011-10-09 11:20:37
【问题描述】:

我想一次性完成以下操作:

SELECT * FROM jobs WHERE status='PENDING';
UPDATE jobs SET status='RUNNING' WHERE status='PENDING';

所以获取所有待处理的作业,然后立即将它们设置为“正在运行”。

我不想在两个语句中一个接一个地执行此操作的原因是,可以在 SELECT 之后但在 UPDATE 之前将作业作为“PENDING”添加到作业表中,因此我最终将作业设置为RUNNING 即使它处于 PENDING 状态时我还没有抓住它。

有没有办法合二为一?所以我希望 SELECT 和 UPDATE 的结果即时发生。

谢谢。

【问题讨论】:

    标签: postgresql select


    【解决方案1】:

    为什么不使用 RETURNING 子句并在一个语句中处理这两个事情:

    UPDATE jobs 
        SET status='RUNNING' 
    WHERE status='PENDING'
    RETURNING *
    

    这样,您将通过单个原子操作获取 UPDATE 更改的所有行。

    【讨论】:

    【解决方案2】:
    begin;
    select * 
    from jobs 
    where status='pending'
    for update
    ;
    update jobs 
    set status='running' 
    where status='pending';
    commit;
    

    【讨论】:

    • 我认为这些语句作为一个单元执行,第一条语句的输出是更新的输入,但我错了。如果您在第一个语句中添加了一个“AND”子句并在第二个语句中忽略了它......您将在数据库中进行灾难性的更新。仅在没有“提交”关键字的情况下尝试此操作,否则您会弄乱您的数据
    • 就我而言,选择查询在表和计算上的连接和内连接非常复杂,我希望更新只是使用这些计算中的值来更新数据库值
    【解决方案3】:

    一般来说,您应该使用一个 UPDATE 语句来完成。 UPDATE 通常不会受到在 UPDATE 语句运行时可能已更改的行的影响,但是,最好阅读事务隔离级别 here

    假设您使用的是“已提交读”的默认设置,它是这样说的:

    Read Committed 是 PostgreSQL 中的默认隔离级别。当一个 事务在此隔离级别上运行,SELECT 查询只能看到 在查询开始之前提交的数据;

    关于更新:

    UPDATE、DELETE、SELECT FOR UPDATE 和 SELECT FOR SHARE 命令 在搜索目标行方面的行为与 SELECT 相同:它们 只会找到在命令开始时提交的目标行 时间。但是,这样的目标行可能已经更新(或 被另一个并发事务删除或锁定) 成立。在这种情况下,可能的更新程序将等待第一个 更新事务以提交或回滚(如果它仍在 进步)。如果第一个更新程序回滚,那么它的效果是 否定,第二个更新程序可以继续更新 最初发现行。如果第一个更新者提交,第二个更新者 如果第一个更新程序删除了该行,将忽略该行,否则它将 尝试将其操作应用于行的更新版本。这 命令的搜索条件(WHERE 子句)被重新评估为 查看该行的更新版本是否仍然与搜索匹配 健康)状况。如果是这样,第二个更新程序继续其操作, 从行的更新版本开始。 (在选择的情况下 FOR UPDATE 和 SELECT FOR SHARE,表示它是更新版本 被锁定并返回给客户端的行。)

    所以在你的场景中,一个 UPDATE 应该没问题。

    还要记住,有一个称为SELECT FOR UPDATE 的语句,它将锁定您选择的行。你可以阅读here。 您需要使用此功能的场景是在预订系统中。考虑这个例子:

    1. 执行SELECT 以查明房间 XYZ 是否可用于日期 X 的预订。
    2. 房间可用。执行UPDATE查询预订房间。

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

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

    但同样,在您的场景中,不需要此 SELECT FOR UPDATE,因为您在一个语句中执行所有操作并且没有提前检查任何内容。

    【讨论】:

      猜你喜欢
      • 2012-12-26
      • 1970-01-01
      • 1970-01-01
      • 2021-01-04
      • 2014-08-26
      • 1970-01-01
      • 2016-03-14
      • 2013-10-15
      • 2014-07-17
      相关资源
      最近更新 更多