我有一个简单的PL / PGSQL块 Postgres 9.5 ,该块循环遍历表中的记录并有条件地更新一些记录。
这是一个简化的示例:
DO $$
DECLARE
-- Define a cursor to loop through records
my_cool_cursor CURSOR FOR
SELECT
u.id AS user_id,
u.name AS user_name,
u.email AS user_email
FROM users u
;
BEGIN
FOR record IN my_cool_cursor LOOP
-- Simplified example:
-- If user's first name is 'Anjali', set email to NULL
IF record.user_name = 'Anjali' THEN
BEGIN
UPDATE users SET email = NULL WHERE id = record.user_id;
END;
END IF;
END LOOP;
END;
$$ LANGUAGE plpgsql;
我想直接针对我的数据库(从我的应用程序,通过控制台等)执行此块。我不想要创建FUNCTION()
或存储过程来执行此操作。
问题是CURSOR
和LOOP
在我的users
表上创建了一个表级锁,因为外部BEGIN...END
之间的所有事务都在事务中运行。这将阻止针对它的任何其他未决查询。如果users
足够大,则会将其锁定几秒钟甚至几分钟。
我尝试在每个COMMIT
之后进行UPDATE
,以便它定期清除事务和锁定。我很惊讶地看到以下错误消息:
ERROR: cannot begin/end transactions in PL/pgSQL
HINT: Use a BEGIN block with an EXCEPTION clause instead.
我不太确定如何完成此操作。是否要求我举起EXCEPTION
来强制COMMIT
?我尝试阅读the documentation on Trapping Errors,但只提到了ROLLBACK
,所以看不到COMMIT
的任何方式。
COMMIT
内定期LOOP
进行交易?答案 0 :(得分:1)
您不能在PostgreSQL function
或DO
命令 (plpgsql或任何其他PL)中glUniform()
。您报告的错误消息到此为止(就Postgres 9.5而言):
COMMIT
procedure
可以在Postgres 11或更高版本中实现。参见:
在较早的版本中,实现“自主交易”的解决方法有限:
但是在本案例中,您不需要任何一个。
改为使用简单的ERROR: cannot begin/end transactions in PL/pgSQL
:
UPDATE
仅锁定实际更新的行(带有例外情况)。而且由于整个表的速度比UPDATE users
SET email = NULL
WHERE user_name = 'Anjali'
AND email IS DISTINCT FROM NULL; -- optional improvement
快得多,因此锁定也非常简短。
添加的CURSOR
避免空更新。相关:
在ppgsql函数中很少使用显式游标。
答案 1 :(得分:0)
如果要避免长时间锁定行,则还可以定义游标WITH HOLD
,例如使用DECLARE
SQL语句。
可以在事务边界上使用此类游标,因此可以在进行一定数量的更新后COMMIT
。您要付出的代价是必须在数据库服务器上实现游标。
由于不能在函数中使用事务处理语句,因此您将不得不使用过程或提交应用程序代码。