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

PL/pgSQL批量导CSV首次循环后cursor不存在错误的原因与解决方法

错误原因

你遇到的cursor "curs" does not exist错误核心是事务提交导致游标失效:

  • PostgreSQL中显式声明的游标和当前事务强绑定,事务一旦执行COMMIT提交,事务上下文内打开的所有游标都会被自动关闭。
  • 你在循环内部写入了COMMIT语句,第一个文件导入完成提交后,提前打开的curs游标就被销毁了,第二次循环执行FETCH curs时自然找不到对应游标。

另外你的代码还存在两处隐性语法问题会导致执行报错:

  1. 插入数据的SELECT子句中,mytemp.dataB和mytemp.source_id之间缺少逗号分隔,会触发语法错误。
  2. 开头DROP TABLE mytemp没有加IF EXISTS判断,第一次执行时临时表不存在,会直接抛出错误。
修复方案

根据你的需求分两种场景处理:

场景1:所有文件导入要么全部成功要么全部回滚

直接删除循环内部的COMMIT语句即可,DO匿名块默认会在所有逻辑执行完成后自动提交事务,游标会在整个事务周期内保持有效。

场景2:允许单个文件导入失败不影响其他文件,导入成功的文件直接标记为已加载

DO匿名块不支持内部事务控制,你需要改用**存储过程(PROCEDURE)**实现,同时抛弃显式游标写法,改用PostgreSQL自带的FOR ... IN SELECT隐式游标循环,避免游标和事务绑定的问题。同时建议优化以下细节:

  • 用DROP TABLE IF EXISTS避免临时表不存在的报错
  • FORMAT函数中用%L格式化文件路径,自动转义特殊字符,避免路径异常或SQL注入风险
修正后完整代码

全量事务版本(适合文件量小、不需要部分提交的场景)

DO $$
DECLARE
    file_record record;
    curs CURSOR
        FOR SELECT id, file_path
            FROM source_files
            WHERE added_to_db=FALSE
            ORDER BY id;
BEGIN
    OPEN curs;
    LOOP
        FETCH curs INTO file_record;
        exit WHEN NOT found;
        BEGIN
            DROP TABLE IF EXISTS mytemp;

            CREATE TABLE mytemp
            (
                dataA numeric,
                dataB numeric
            );

            EXECUTE FORMAT('COPY mytemp
                            FROM %L
                            DELIMITER '',''
                            CSV HEADER;', file_record.file_path);

            ALTER TABLE mytemp
                ADD COLUMN source_id int;

            UPDATE mytemp 
                SET source_id=file_record.id;

            INSERT INTO data_table(
                dataA,
                dataB,
                source_id
            )
            SELECT 
                mytemp.dataA,
                mytemp.dataB,
                mytemp.source_id
            FROM 
                mytemp;

            UPDATE source_files
                SET added_to_db=TRUE WHERE CURRENT OF curs;
        END;
    END LOOP;
    CLOSE curs;
END $$;

支持部分提交的存储过程版本(适合文件量大、允许部分导入的场景)

-- 先创建存储过程
CREATE OR REPLACE PROCEDURE batch_import_csv()
LANGUAGE plpgsql
AS $$
DECLARE
    file_record record;
BEGIN
    -- 用隐式游标循环,不需要手动管理游标生命周期
    FOR file_record IN SELECT id, file_path FROM source_files WHERE added_to_db=FALSE ORDER BY id LOOP
        BEGIN
            DROP TABLE IF EXISTS mytemp;

            CREATE TABLE mytemp
            (
                dataA numeric,
                dataB numeric
            );

            EXECUTE FORMAT('COPY mytemp
                            FROM %L
                            DELIMITER '',''
                            CSV HEADER;', file_record.file_path);

            ALTER TABLE mytemp
                ADD COLUMN source_id int;

            UPDATE mytemp 
                SET source_id=file_record.id;

            INSERT INTO data_table(
                dataA,
                dataB,
                source_id
            )
            SELECT 
                mytemp.dataA,
                mytemp.dataB,
                mytemp.source_id
            FROM 
                mytemp;

            UPDATE source_files
                SET added_to_db=TRUE WHERE id = file_record.id;
            
            -- 单个文件导入成功就提交
            COMMIT;
        EXCEPTION
            -- 单个文件导入失败就回滚当前文件的操作,继续下一个文件
            WHEN OTHERS THEN
                ROLLBACK;
                RAISE NOTICE '导入文件%失败,错误信息:%', file_record.file_path, SQLERRM;
        END;
    END LOOP;
END $$;

-- 调用存储过程执行导入
CALL batch_import_csv();

内容的提问来源于stack exchange,提问作者sev

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.21 08:24:04