我有两个线程,都需要更新同一行 该行看起来像:
更新如下:
Update table set lastName = "yyy" where id = 1 and lastName = null;
我想要的是如果一个线程成功地将null lastName更新为新值,我希望第二个线程失败并将某种异常抛回给调用者。我需要一种方法来在更新期间知道该列不再为null(这意味着它已由第一个线程更新)
哪个更新声明会解决我的问题? (select for update
,coalesce
等。)
答案 0 :(得分:0)
SQL查询本身很好,只需将其包装在可序列化的事务中:
SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;
在此隔离级别中,您可能会通过中止或重试事务来选择要处理的应用程序代码中的错误。
更多信息:
答案 1 :(得分:0)
您可以在默认的读提交隔离中使用它。
表示exp:
SESSION A :
digoal=# create table t_curr(id int, c1 text, c2 text);
CREATE TABLE
digoal=# insert into t_curr values (1,'t',null);
INSERT 0 1
digoal=# begin;
BEGIN
digoal=# update t_curr set c2='ttt' where id=1 and c2 is null;
UPDATE 1
SESSION B :
digoal=# update t_curr set c2='ttwt' where id=1 and c2 is null;
SESSION A :
digoal=# end;
COMMIT
SESSION B :
UPDATE 0
digoal=# select * from t_curr ;
id | c1 | c2
----+----+-----
1 | t | ttt
(1 row)
但是如果你想在更新零行时引发错误,你应该使用可重复的读隔离。 对于exp:
SESSION A :
digoal=# update t_curr set c2 = null;
UPDATE 1
digoal=# begin transaction isolation level repeatable READ ;
BEGIN
digoal=# update t_curr set c2='ttt' where id=1 and c2 is null;
UPDATE 1
SESSION B :
digoal=# begin transaction isolation level repeatable READ ;
BEGIN
digoal=# update t_curr set c2='ttt' where id=1 and c2 is null;
SESSION A :
digoal=# end;
COMMIT
SESSION B :
ERROR: could not serialize access due to concurrent update
或者你使用函数来更新,并引发错误。 对于exp:
SESSION a :
digoal=# update t_curr set c2 = null;
UPDATE 1
digoal=# begin;
BEGIN
digoal=# do language plpgsql $$
declare
begin
update t_curr set c2='ttt' where id=1 and c2 is null;
if not found then
raise 'update 0';
end if;
end;
$$;
DO
SESSION B :
digoal=# begin;
BEGIN
digoal=# do language plpgsql $$
declare
begin
update t_curr set c2='ttt' where id=1 and c2 is null;
if not found then
raise 'update 0';
end if;
end;
$$;
SESSION A :
digoal=# end;
COMMIT
SESSION B :
ERROR: update 0
digoal=# end;
ROLLBACK
答案 2 :(得分:0)
这可以使用default isolation level READ COMMITTED
中的常规UPDATE
命令完成。所有这些对于并发使用都是安全的。
你可以使用TRANSACTION ISOLATION LEVEL SERIALIZABLE
。这将自动生成序列化失败异常。但这使得整个交易变得更加昂贵。请改用默认隔离级别中的以下方法之一:
您的原始命令有两个错误:
'yyy'
。something = NULL
始终为NULL
。请改用something IS NULL
。 Details in the manual.
UPDATE tbl
SET lastname = 'yyy'
WHERE tbl_id = 1
AND lastname IS NULL;
它会告诉您受影响的行数:
UPDATE count
只有第一个UPDATE
实际上会更新该行。以下所有尝试都不执行任何操作并返回UPDATE 0
。
RETURNING
clause UPDATE
可以直接返回行:
UPDATE tbl
...
RETURNING lastname; -- value from *new* row
只有第一个UPDATE
会返回一个值。以下全部返回无行。
如果您确实需要例外:
CREATE OR REPLACE FUNCTION f_upd(_tbl_id int, _lastname text)
RETURNS void AS
$func$
BEGIN
UPDATE tbl
SET lastname = $2
WHERE tbl_id = $1
AND lastname IS NULL;
IF NOT FOUND
RAISE EXCEPTION 'UPDATE found no row.';
END IF;
END
$func$ LANGUAGE plpgsql