在我的代码的不同部分,我必须在异常后重试事务。但我无法弄清楚如何做到这一点。 这是我的测试功能:
CREATE OR REPLACE FUNCTION f() RETURNS VOID AS $$
DECLARE
user_cur CURSOR FOR SELECT * FROM "user" WHERE id < 50 limit 10;
row RECORD;
counter INTEGER DEFAULT 0;
dummy INTEGER DEFAULT 0;
BEGIN
RAISE INFO 'Start... ';
OPEN user_cur;
LOOP
FETCH user_cur INTO row;
EXIT WHEN row IS NULL;
BEGIN
UPDATE "user" SET dummy = 'dummy' WHERE id = row.id;
counter := counter + 1;
dummy := 10 / (5 % counter);
RAISE NOTICE 'dummy % , user_id %', (5 % counter), row.id;
EXCEPTION WHEN division_by_zero THEN
--What should I do here to retry transaction?
END;
END LOOP;
RAISE INFO 'Finished.';
RETURN;
END;
$$ LANGUAGE plpgsql;
答案 0 :(得分:4)
对于您要做的事情,LOOP inside the LOOP将是一个合适的解决方案。无需昂贵的异常处理。
测试设置:
CREATE TEMP TABLE usr (id int, dummy text);
INSERT INTO usr VALUES
(1,'foo')
,(2,'bar')
,(3,'baz')
,(4,'blarg');
功能:
CREATE OR REPLACE FUNCTION x.foo()
RETURNS VOID AS
$BODY$
DECLARE
_r record;
_dummy integer := 0;
_ct integer := 0;
_5mod_ct integer;
BEGIN
RAISE INFO 'Start... ';
FOR _r IN
SELECT * FROM usr WHERE id < 50 LIMIT 10
LOOP
LOOP
UPDATE usr SET dummy = 'foo' WHERE id = _r.id;
_ct := _ct + 1;
_5mod_ct := 5 % _ct;
EXIT WHEN _5mod_ct > 0; -- make sure this will be TRUE eventually!
RAISE INFO '_5mod_ct = 0; repeating UPDATE!';
END LOOP;
_dummy := 10 / _5mod_ct;
RAISE NOTICE '_5mod_ct: %, _dummy: %, user.id: %',
_5mod_ct, _dummy, _r.id;
END LOOP;
RAISE INFO 'Finished.';
END;
$BODY$ LANGUAGE plpgsql;
致电:
SELECT foo()
输出:
INFO: Start...
INFO: _5mod_ct = 0; repeating UPDATE!
NOTICE: _5mod_ct: 1, _dummy: 10, user.id: 1
NOTICE: _5mod_ct: 2, _dummy: 5, user.id: 2
NOTICE: _5mod_ct: 1, _dummy: 10, user.id: 3
INFO: _5mod_ct = 0; repeating UPDATE!
NOTICE: _5mod_ct: 5, _dummy: 2, user.id: 4
INFO: Finished.
您的代码示例显示了许多问题:
请勿使用保留字作为标识符。 user
特别是reserved word in every SQL standard and in PostgreSQL。将您的表称为“用户”是不好的做法。双引号使它成为可能。但这并不意味着这是一个好主意。
不要声明与在函数体中使用的表列同名的变量。这很容易导致命名冲突。 dummy
在您的示例中同时是变量和列名。只是另一个装脚枪。一种(任意)可能性是使用_
为变量添加前缀,就像我演示的那样。
像我演示的FOR
loop比显式游标处理简单得多。
答案 1 :(得分:2)
感谢上述评论,我找到了解决方案:
CREATE OR REPLACE FUNCTION f() RETURNS VOID AS $$
DECLARE
row RECORD;
counter INTEGER DEFAULT 0;
dummy INTEGER DEFAULT 0;
BEGIN
-- Clear user rating
RAISE INFO 'Start... ';
FOR row IN SELECT * FROM user_prop WHERE id < 50 limit 10 LOOP
LOOP
BEGIN
UPDATE user_prop SET some_field = 'whatever' WHERE id = row.id;
counter := counter + 1;
dummy := 10 / (5 % counter);
-- exit nested loop if no exception
EXIT;
EXCEPTION
WHEN division_by_zero THEN
-- do nothing, just repeat the loop
WHEN deadlock_detected THEN
-- do nothing, just repeat the loop
END;
END LOOP;
END LOOP;
RAISE INFO 'Finished.';
RETURN;
END;
$$ LANGUAGE plpgsql;