使用 EXCEPT 集合运算符时如何编写 Postgres SELECT FOR UPDATE?

How to write a Postgres SELECT FOR UPDATE when using the EXCEPT set operator?

在 Postgres(11,如果重要的话)中,我需要做一个 SELECT FOR UPDATE 来获取行的集合,我随后将对其进行一些更改,并且我不希望任何人在我进行这些更改时,在我的交易之外搞乱了。

但是,我要锁定的行集实际上是由一个集差定义的,即

SELECT <columns> FROM table1 t1 JOIN table2 t2 ON ... WHERE ...
EXCEPT
SELECT <columns> FROM table1 t1 JOIN table3 t3 ON ... WHERE ...

我想要这个集合差异的结果集来确定被锁定的行集合;也就是说,那些被第二个 SELECT select 编辑的行在理想情况下应该 而不是 被锁定。

但我不太确定将 FOR UPDATE 子句放在哪里才能实现此目的。似乎将 FOR UPDATE 紧跟在上面任何 SELECT 行之后不会给我想要的东西。事实上,我怀疑我 不能 合法地将它放在 SELECT 行的第一行之后(即,就在 EXCEPT 之前)。

我想到的一个想法是用括号括起第二个 SELECTEXCEPT 的主题),这样 FOR UPDATE 就不会被解释为那一秒的一部分 SELECT:

SELECT <columns> FROM table1 t1 JOIN table2 t2 ON ... WHERE ...
EXCEPT
(SELECT <columns> FROM table1 t1 JOIN table3 t3 ON ... WHERE ...)
FOR UPDATE

但我也不确定这是否能满足我的要求,即使它在语法上是可以接受的。

如果我对 (Postgres) select 语句的解析树的形状有一个想法,我自己可以很容易地弄清楚;但实际上,我现在有点迷路了。

您不能将 FOR UPDATEUNIONINTERSECTEXCEPT 一起使用,因为这在一般情况下会导致歧义。

我可以想到两种方法:

  1. 使用EXISTSNOT EXISTS:

    SELECT ... FROM table1
    WHERE EXISTS (SELECT 1 FROM table2 ...
                  WHERE table2.x = table1.x AND ...)
      AND NOT EXISTS (SELECT 1 FROM table3 ...
                      WHERE table3.y = table1.y AND ...)
    FOR UPDATE OF table1;
    
  2. 使用子查询:

    SELECT ... FROM table1
    WHERE id IN (SELECT t1.id
                 FROM table1 t1 JOIN table2 t2 ON ...
                 WHERE ...
                 EXCEPT
                 SELECT t1.id
                 FROM table1 t1 JOIN table3 t3 ON ...
                 WHERE ...)
    FOR UPDATE OF table1;