2016-09-17 67 views
2

更新:問題已解決!SELECT FOR UPDATE返回零行

這是PostgreSQL中的bug。 Tom Lane將其固定在這個 commit


爲什麼SELECT FOR UPDATE在下面的場景中返回0行?但如果我只是從第二個事務執行sql查詢,它總是返回1行。

TRANSACTION 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. 

TRANSACTION 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 

TRANSACTION 1:

COMMIT; 

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

TRANSACTION 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); 
+0

難道你不能提供一個最小的完整例子嗎?即在必要時包含您的'create table'語句和一些'insert'語句。 – redneb

+0

@redneb: CREATE TABLE t1(_pk serial,t1c1 integer,t1c2 integer,t1c3 text); CREATE TABLE t2(_pk serial,t2c1 text,t2c2 integer); 插入t1(t1c1,t1c2,t1c3)的值(123456789,100,'string_value_1'); 插入到t2(t2c1,t2c2)值('string_value_2',100); –

回答

0

我想說這是PostgreSQL中的一個錯誤,你應該報告它。

+0

我是對的; Tom Lane在[此提交]中修復了它(http://git.postgresql.org/pg/commitdiff/96dd77d349424f270d129f8f40e75f762ddcca7d)。 –

-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 
+0

[Documentation](https://www.postgresql.org/docs/9.4/static/sql-select.html)表示可以在WITH查詢中使用「FOR UPDATE」: 如果將鎖定子句應用於一個視圖或子查詢,它會影響視圖或子查詢中使用的所有表。但是,這些子句不適用於主查詢引用的WITH查詢。如果要在WITH查詢中發生行鎖定,請在WITH查詢中指定鎖定子句。 –

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) 

外「CTE掃描上CTE2」似乎在「LockRows」一步丟棄仍然存在該行。已知Postgres在獲取鎖後重新評估where子句(請參見this example with work queues)。查詢計劃可能包含where子句,該子句用於不可見的ctid行標識符,該標識符在任何UPDATE之後都會更改?

我要求this question上的Postgres mailing list看看其他人是否能夠澄清這裏發生了什麼。