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
)
这在 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;
如果您在并发写入负载下执行此操作,则子查询(确定行的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
事务隔离。
参见: