SELECT FOR UPDATE返回零行

时间:2016-09-17 18:47:40

标签: postgresql transactions

更新:问题已解决!

这是PostgreSQL中的bug。汤姆莱恩修好了这个 commit

为什么SELECT FOR UPDATE在下面的场景中返回0行?但是,如果我只是从第二个事务执行sql查询,它总是返回1行。

交易1:

BEGIN;
-- This query updates t1c1 to its current value, it doesn't change anything
UPDATE t1 SET t1c3 = 'string_value_1' WHERE t1c1 = 123456789;

-- Query returned successfully: one row affected, 51 msec execution time.

交易2:

WITH 
    cte1 AS (
        SELECT t2c2 FROM t2 WHERE t2c1 = 'string_value_2'
    ),

    cte2 AS (
        SELECT * FROM t1
        WHERE 
             t1c1 = 123456789 
            AND t1c2 = (SELECT t2c2 FROM cte1)
        FOR UPDATE
    ) 

SELECT * FROM cte2

-- Waiting

交易1:

COMMIT;

-- Query returned successfully with no result in 41 msec.

交易2:

-- Returned 0 rows

示例:

CREATE TABLE t1 (_pk serial, t1c1 integer, t1c2 integer, t1c3 text);
CREATE TABLE t2 (_pk serial, t2c1 text, t2c2 integer);
insert into t1 (t1c1, t1c2, t1c3) values(123456789, 100, 'string_value_1');
insert into t2 (t2c1, t2c2) values('string_value_2', 100);

3 个答案:

答案 0 :(得分:1)

有趣的问题!使用explain verbose analyze,我得到以下查询计划:

                                                    QUERY PLAN                                                     
-------------------------------------------------------------------------------------------------------------------
 CTE Scan on cte2  (cost=51.13..51.15 rows=1 width=44) (actual time=4544.488..4544.488 rows=0 loops=1)
   Output: cte2._pk, cte2.t1c1, cte2.t1c2, cte2.t1c3
   CTE cte1
     ->  Seq Scan on public.t2  (cost=0.00..24.50 rows=6 width=4) (actual time=0.002..0.003 rows=1 loops=1)
           Output: t2.t2c2
           Filter: (t2.t2c1 = 'string_value_2'::text)
   CTE cte2
     ->  LockRows  (cost=0.12..26.63 rows=1 width=50) (actual time=4544.485..4544.485 rows=0 loops=1)
           Output: t1._pk, t1.t1c1, t1.t1c2, t1.t1c3, t1.ctid
           InitPlan 2 (returns $1)
             ->  CTE Scan on cte1  (cost=0.00..0.12 rows=6 width=4) (actual time=0.005..0.006 rows=1 loops=1)
                   Output: cte1.t2c2
           ->  Seq Scan on public.t1  (cost=0.00..26.50 rows=1 width=50) (actual time=0.018..0.019 rows=1 loops=1)
                 Output: t1._pk, t1.t1c1, t1.t1c2, t1.t1c3, t1.ctid
                 Filter: ((t1.t1c1 = 123456789) AND (t1.t1c2 = $1))
 Planning time: 0.116 ms
 Execution time: 4544.535 ms
(17 rows)

cte2"外部" CTE扫描似乎放弃了在" LockRows"步。已知Postgres在获取锁之后重新评估where子句(请参阅this example with work queues。)查询计划可能在不可见的where行标识符上包含ctid子句,在UPDATE之后发生了什么变化?

我已在this questionPostgres mailing list询问其他人是否能够澄清此处发生的事情。

答案 1 :(得分:0)

我说这是PostgreSQL中的一个错误,你应该报告它。

答案 2 :(得分:-1)

FOR UPDATE在CTE中没有任何意义。这应该工作:

WITH 
cte1 AS (
    SELECT t2c2 FROM t2 WHERE t2c1 = "string_value_2"
),

cte2 AS (
    SELECT * FROM t1
    WHERE 
         t1c1 = 123456789 
        AND t1c2 = (SELECT t2c2 FROM cte1)
) 

SELECT * FROM cte2  FOR UPDATE