在PostgreSQL中使用SELECT FOR UPDATE SELECT INTO

时间:2014-10-04 17:32:59

标签: sql postgresql transactions plpgsql isolation-level

假设我有message关系,我保存使用此命令创建的消息:

CREATE TABLE message 
(
     id serial primary key, 
     foo1 integer, 
     foo2 integer, 
     foo3 text
)

我们有一个函数可以获取消息并从关系中删除它,如下所示:

CREATE FUNCTION get_and_delete_message(p_foo1 integer)
RETURNS TABLE(r_id integer, r_foo1 integer, r_foo2 integer, r_foo3 text) AS $$
DECLARE
  message_id integer;
BEGIN
    SELECT id INTO message_id FROM message WHERE foo1 = p_foo1 LIMIT 1;
    RETURN QUERY SELECT * FROM message WHERE foo1 = p_foo1 LIMIT 1;
    DELETE FROM message where id = message_id;
END;
$$ LANGUAGE plpgsql;

假设READ_COMMITTED隔离级别,可能是来自两个用户的两个并发事务返回相同的消息,尽管显然只有一个删除/获取它。这不是我的应用程序所需的行为,我希望只有一个用户可以读取一条消息。

假设REPEATABLE_READ这不会发生。

但在阅读FOR UPDATE后,我想也许仍然可以使用READ_COMMITTED级别并更改get_and_delete_message函数,如下所示:

   ...
    BEGIN
        SELECT id INTO message_id FROM message WHERE foo1 = p_foo1 LIMIT 1;
        RETURN QUERY SELECT * FROM message WHERE foo1 = p_foo1 LIMIT 1 FOR UPDATE;
        DELETE FROM message where id = message_id;
    END;
    ...

根据我的理解,在第二个FOR UPDATE中使用SELECT实际上将锁定返回的行直到事务结束,因此如果我们有2个并发事务,则只有一个实际上会返回并删除消息

是这样的吗?或者我也应该SELECT id INTO message_id FROM message WHERE foo1 = p_foo1 LIMIT 1 FOR UPDATE吗?我无法找到有关SELECT INTOFOR UPDATE合并的任何信息。有什么想法?

1 个答案:

答案 0 :(得分:1)

您可以在一个语句中执行此操作,无需选择:

CREATE FUNCTION get_and_delete_message(p_foo1 integer)
RETURNS TABLE(r_id integer, r_foo1 integer, r_foo2 integer, r_foo3 text) 
AS $$
  DELETE FROM message 
    where foo1 = p_foo1 
  returning *;
END;
$$ LANGUAGE sql;

这将删除行,然后根据delete语句返回所有已删除的行。