首页 文章

如果不为null,则选择更新,但带有异常或返回值

提问于
浏览
0

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

  • (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

    您可以在默认的读提交隔离中使用它 .

    对于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
    
  • 0

    SQL查询本身很好,只需将其包装在可序列化的事务中:

    SET TRANSACTION ISOLATION LEVEL SERIALIZABLE;

    在此隔离级别中,您可能会通过中止或重试事务来选择要处理的应用程序代码中的错误 .

    更多信息:

  • 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子句

    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
    

相关问题