PostgreSQL - 在函数中启动事务块

YCF*_*F_L 5 sql postgresql plpgsql

我正在尝试在函数内创建一个事务块,所以我的目标是一次使用这个函数,所以如果有人使用这个函数而另一个人想使用它,他不能​​直到第一个完成我创建了这个函数:

CREATE OR REPLACE FUNCTION my_job(time_to_wait integer) RETURNS INTEGER AS $$
DECLARE
  max INT;
BEGIN
   BEGIN;
       SELECT MAX(max_value) INTO max FROM sch_lock.table_concurente;
       INSERT INTO sch_lock.table_concurente(max_value, date_insertion) VALUES(max + 1, now());
       -- Sleep a wail
       PERFORM pg_sleep(time_to_wait);
       RETURN max;
   COMMIT; 
END;
$$ 
LANGUAGE plpgsql;
Run Code Online (Sandbox Code Playgroud)

但它接缝不起作用,我有一个错误语法错误 BEGIN;

没有BEGIN;并且COMMIT我得到了正确的结果,我使用这个查询来检查:

-- First user should to wait 10 second
SELECT my_job(10) as max_value;

-- First user should to wait 3 second
SELECT my_job(3) as max_value;
Run Code Online (Sandbox Code Playgroud)

所以结果是:

+-----+----------------------------+------------+
|  id |              date          | max_value  |
+-----+----------------------------+------------+
|  1  | 2017-02-13 13:03:58.12+00  |      1     |
+-----|----------------------------+------------+
|  2  | 2017-02-13 13:10:00.291+00 |      2     |
+-----+----------------------------+------------+
|  3  | 2017-02-13 13:10:00.291+00 |      2     |
+-----+----------------------------+------------+
Run Code Online (Sandbox Code Playgroud)

但结果应该是:

+-----+----------------------------+------------+
|  id |              date          | max_value  |
+-----+----------------------------+------------+
|  1  | 2017-02-13 13:03:58.12+00  |      1     |
+-----|----------------------------+------------+
|  2  | 2017-02-13 13:10:00.291+00 |      2     |
+-----+----------------------------+------------+
|  3  | 2017-02-13 13:10:00.291+00 |      3     |
+-----+----------------------------+------------+
Run Code Online (Sandbox Code Playgroud)

所以第三个id = 3应该有max_value = 3and not 2,这是因为第一个用户选择 max = 1 并等待10 sec,第二个用户选择 max = 1 并3 sec在插入前等待,但正确的解决方案是:我不能使用这个函数直到第一个完成,为此我想制作一些安全和受保护的东西。

我的问题是:

  • 如何在函数内创建事务块?
  • 您对我们如何以安全的方式做到这一点有什么建议吗?

谢谢你。

Chr*_*ers 4

好吧,所以你不能COMMIT在函数中。但是,您可以拥有一个保存点并回滚到该保存点。

最小的可能事务是服务器从客户端解析和执行的单个语句,因此每个事务都是一个函数。然而,在一次交易中,您可以拥有保存点。在这种情况下,您将查看 PostgreSQL 的异常处理部分来处理此问题。

但这不是您想要的。您希望(我认为?)数据在长时间运行的服务器端操作期间可见。为此,你有点不走运。在运行函数时,您无法真正增加事务 ID。

您有几个选择,按照我认为的良好实践的顺序(最好到最差):

  1. 将逻辑分解为更小的部分,每个部分将数据库从一种一致状态移动到另一种一致状态,并在单独的事务中运行这些部分。
  2. 在数据库中使用消息队列(如 pg_message_queue),加上外部工作线程,以及运行一个步骤并为下一步生成消息的东西。缺点是这增加了更多的维护工作。
  3. 使用 dblink 或 pl/python 或 pl/perlu 等函数或框架连接回数据库并在那里运行事务。恶心……