select for update如果不为null,则带有异常或返回值

时间:2014-04-11 18:11:44

标签: sql postgresql concurrency sql-update

我有两个线程,都需要更新同一行 该行看起来像:

  • (id,firstName,lastName)
  • (1,“xxxx”,null)
  • 1是主键值

更新如下:

Update table set lastName = "yyy" where id = 1 and lastName = null;

我想要的是如果一个线程成功地将null lastName更新为新值,我希望第二个线程失败并将某种异常抛回给调用者。我需要一种方法来在更新期间知道该列不再为null(这意味着它已由第一个线程更新)

哪个更新声明会解决我的问题? (select for updatecoalesce等。)

3 个答案:

答案 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 NULLDetails in the manual.

UPDATE tbl
SET    lastname = 'yyy'
WHERE  tbl_id = 1
AND    lastname IS NULL;

Command tag

它会告诉您受影响的行数:

UPDATE count

只有第一个UPDATE实际上会更新该行。以下所有尝试都不执行任何操作并返回UPDATE 0

RETURNING clause

UPDATE可以直接返回行:

UPDATE tbl
...
RETURNING lastname; -- value from *new* row

只有第一个UPDATE会返回一个值。以下全部返回无行

PL / pgSQL函数引发异常

如果您确实需要例外:

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