如果表B中不存在引用行,则更新表A,限制更新

问题描述 投票:0回答:2

table1 的 PK 是 table2 中的 FK。

我想更新table1中table2中没有记录的状态,并限制更新次数。表2中可能没有记录。

类似:

UPDATE t1
SET status = 0
WHERE NOT EXISTS (
    SELECT id
    FROM t2
    WHERE t1.id = t2.id
    LIMIT 1000
)
sql postgresql sql-update
2个回答
1
投票

这在 Postgres 中有点复杂,因为没有

limit
。 假设您在
t1
中有一个主键(我假设是
id
),您可以使用子查询来确定要更新的行,然后在
WHERE
子句中进行匹配:

UPDATE t1
    SET status = 0
    FROM (SELECT tt1.*
          FROM t1 tt1
          WHERE NOT EXISTS (SELECT t2.id FROM t2 WHERE tt1.id = t2.id)
          LIMIT 1000
         ) ttl
    WHERE t1.id = tt1.id;

1
投票

如果您在并发写入负载下执行此操作,则子查询(确定行的SELECT)和外部

UPDATE
之间存在
竞争条件
,这可能会导致错误的结果。为了防止这种情况,请添加行锁定子句。

但是,这需要在 CTE 中完成才能可靠(至少在我的 Postgres 版本 10 的测试中)。所以:

WITH cte AS (
   SELECT id      -- PK
   FROM   t1
   WHERE  NOT EXISTS (SELECT FROM t2 WHERE t2.id = t1.id)
   LIMIT  1000
   FOR    UPDATE  -- SKIP LOCKED -- ?
   )
UPDATE t1
SET    status = 0
FROM   cte
WHERE  t1.id = cte.id
RETURNING id;     -- optional

如果您运行多个这样的命令(可能是并行的),请添加

SKIP LOCKED
,这样它们就不会互相阻塞。

这仅保护

t1
中的现有行。仍然存在一个问题,即在
t2
SELECT
之间可能会添加冲突的行。
您提到了 FK 约束。当父行上有 

UPDATE

锁时,我从头到尾不确定

t2
中的依赖行是否会被 FK 约束阻止添加。必须测试,但现在没时间了。
Postgres 没有针对用户命令的谓词锁定。 (用户只能锁定现有行。)为了绝对确定,您还可以使用(更昂贵的)

FOR UPDATE

事务隔离。

参见:

    Postgres 更新…限制 1
© www.soinside.com 2019 - 2024. All rights reserved.