You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 03:53:24