PLPGSQL存储过程遇异常后无法继续循环问题咨询
PLPGSQL循环中异常后继续执行的问题解决
核心结论
PLPGSQL完全支持在循环内捕获异常后继续执行下一轮循环,你的代码框架本身是正确的,问题出在临时表的处理逻辑上。
问题根源
你的代码中,临时表temporary_poc_person的创建未做存在性检查,且异常场景下未清理该表:
- 当某条记录处理抛出异常时,
DROP TABLE temporary_poc_person;语句不会执行,临时表会残留。 - 下一轮循环进入
BEGIN块时,执行CREATE TEMPORARY TABLE会触发**“关系已存在”**的异常,虽然这个异常会被EXCEPTION WHEN OTHERS捕获,但持续触发的重复异常会让你误以为存储过程直接终止(实际循环仍在执行,只是不断抛出通知)。 - 另外,存储过程最后一行的
processed = TRUE;会覆盖异常块中设置的processed = FALSE;,导致最终返回结果永远为TRUE,这是逻辑错误。
修正方案
方案1:移除不必要的临时表(推荐)
临时表在这里完全是多余的,可以直接使用循环中的rec记录进行插入操作,避免临时表带来的问题:
CREATE OR REPLACE PROCEDURE poc_exception_normalise(INOUT processed BOOLEAN) LANGUAGE plpgsql AS $$ DECLARE rec RECORD; BEGIN processed := TRUE; -- 默认设为TRUE,有异常再改为FALSE FOR rec IN SELECT * FROM poc_exception_temp_staging_table petst LOOP BEGIN RAISE INFO '--> record = %', rec.id; INSERT INTO poc_person (id, first_name, last_name) VALUES ( rec.id, rec.data."firstName"::VARCHAR(15), rec.data."lastName"::VARCHAR(15) ); DELETE FROM poc_exception_temp_staging_table WHERE id = rec.id; RAISE INFO '--> DELETED RECORD = %', rec.id; EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'Exception occurred while normalising %', rec.id; DELETE FROM poc_exception_temp_staging_table WHERE id = rec.id; RAISE INFO '--> DELETED RECORD = %', rec.id; processed := FALSE; -- 只要有异常就设为FALSE END; END LOOP; RAISE INFO '--> processing complete !!'; END; $$;
方案2:保留临时表但修复清理逻辑
如果一定要使用临时表,需要确保每次循环都能正确清理:
CREATE OR REPLACE PROCEDURE poc_exception_normalise(INOUT processed BOOLEAN) LANGUAGE plpgsql AS $$ DECLARE rec RECORD; BEGIN processed := TRUE; FOR rec IN SELECT * FROM poc_exception_temp_staging_table petst LOOP BEGIN -- 创建临时表时检查是否存在,避免重复创建报错 CREATE TEMPORARY TABLE IF NOT EXISTS temporary_poc_person ( id INT8 NOT NULL, data SUPER NOT NULL ); -- 清空临时表数据,避免残留上一轮的记录 TRUNCATE TABLE temporary_poc_person; INSERT INTO temporary_poc_person VALUES (rec.id, rec.data); RAISE INFO '--> record = %', rec.id; INSERT INTO poc_person (SELECT tpp.id AS id, tpp.data."firstName"::VARCHAR(15) AS first_name, tpp.data."lastName"::VARCHAR(15) AS last_name FROM temporary_poc_person tpp); DELETE FROM poc_exception_temp_staging_table WHERE id = rec.id; RAISE INFO '--> DELETED RECORD = %', rec.id; EXCEPTION WHEN OTHERS THEN RAISE NOTICE 'Exception occurred while normalising %', rec.id; DELETE FROM poc_exception_temp_staging_table WHERE id = rec.id; RAISE INFO '--> DELETED RECORD = %', rec.id; processed := FALSE; END; -- 无论是否异常,都清理临时表 DROP TABLE IF EXISTS temporary_poc_person; END LOOP; RAISE INFO '--> processing complete !!'; END; $$;
关键说明
- 循环内的
BEGIN...EXCEPTION块是独立的子事务,异常发生时会回滚该块内的未提交操作,然后执行EXCEPTION中的逻辑,之后继续下一轮循环。 - 务必避免在循环内创建未正确清理的对象(如临时表),否则会导致重复创建的异常。
processed变量的赋值逻辑需要调整,确保异常发生后最终返回FALSE。
内容的提问来源于stack exchange,提问作者ManSe
相关产品推荐
相关产品推荐

