我正在尝试在函数内部创建一个事务块,所以我的目标是一次使用这个函数,所以如果有人使用这个函数而另一个想要使用它,他就不能直到第一个一个是完成我创建这个功能:
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;
但它接缝不起作用,我错了语法错误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;
结果是:
+-----+----------------------------+------------+
| 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 |
+-----+----------------------------+------------+
但结果应该是:
+-----+----------------------------+------------+
| 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 |
+-----+----------------------------+------------+
所以第三个id = 3
应该有max_value = 3
而不是2
,这是因为第一个用户选择max = 1并等待10 sec
和第二个用户选择max = 1并在插入前等待3 sec
,但正确的解决方案是:我不能使用此功能直到第一个完成,因为我想要做一些安全的事情,保护。
我的问题是:
谢谢。
答案 0 :(得分:3)
好的,你不能在一个函数中COMMIT
。您可以使用保存点并回滚到保存点。
您可能的最小事务是服务器从客户端解析并执行的单个语句,因此每个事务都是一个函数。但是,在交易中,您可以拥有保存点。在这种情况下,您将查看PostgreSQL的异常处理部分来处理这个问题。
然而,这不是你想要的。您希望(我认为?)数据在长时间运行的服务器端操作期间可见。为此,你有点不走运。在运行函数时,您无法真正增加事务ID。
根据我认为的良好做法(最好到最差),你有几个选择:
答案 1 :(得分:1)
您可以使用dblink。类似的东西:
CREATE OR REPLACE FUNCTION my_job(time_to_wait integer) RETURNS INTEGER AS $$
DECLARE
max INT;
BEGIN
SELECT INTO RES dblink_connect('con','dbname=local');
SELECT INTO RES dblink_exec('con', 'BEGIN');
...
SELECT INTO RES dblink_exec('con', 'COMMIT');
SELECT INTO RES dblink_disconnect('con');
END;
$$
LANGUAGE plpgsql;
答案 2 :(得分:1)
我不知道这是否是一种好方法,但如果我们使用LOCK TABLE
就是这样的话:
CREATE OR REPLACE FUNCTION my_job(time_to_wait integer) RETURNS INTEGER AS $$
DECLARE
max INT;
BEGIN
-- Lock table so no one will use it until the first one is finish
LOCK TABLE sch_lock.table_concurente IN ACCESS EXCLUSIVE MODE;
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());
PERFORM pg_sleep(time_to_wait);
RETURN max;
END;
$$
LANGUAGE plpgsql;
它给了我正确的结果。