如何将Refcursor数据导入含序号列的未知结构临时表?
解决Refcursor动态数据导入带序号临时表的问题
我来帮你搞定这个问题——你的核心需求是把结构未知的refcursor数据导入带序号的临时表,当前代码里的(V_CURS_Rec).*报错,主要是因为PL/pgSQL的静态语法限制,而且你预先硬编码临时表列的做法,和“无法预知结果集结构”的需求本身存在矛盾。下面分两种场景给你针对性的解决方案:
场景1:如果其实你能预知Refcursor的结果集结构
如果你的示例场景就是实际情况(比如确定返回CNAME和CDAY两列),那只需要修正插入语句的写法即可,避免直接拼接标量和record的展开字段:
CREATE OR REPLACE FUNCTION FN_TEST() RETURNS VOID LANGUAGE plpgsql AS $$ DECLARE V_CURS REFCURSOR; V_CURS_Rec RECORD; ITER INTEGER; BEGIN CREATE TEMPORARY TABLE IF NOT EXISTS TMP_TBL ( INDX INTEGER NOT NULL, CNAME VARCHAR(20), CDAY VARCHAR(20) ); DELETE FROM TMP_TBL; SELECT * FROM FN_RET_REFCURSOR() INTO V_CURS; ITER := 1; LOOP FETCH V_CURS INTO V_CURS_Rec; EXIT WHEN NOT FOUND; -- 修正:明确指定字段,避免record展开的语法问题 INSERT INTO TMP_TBL (INDX, CNAME, CDAY) VALUES (ITER, V_CURS_Rec.CNAME, V_CURS_Rec.CDAY); ITER := ITER + 1; END LOOP; RETURN; END; $$;
场景2:完全无法预知Refcursor的结果集结构(核心需求)
如果refcursor的列数、列名、类型都不确定,就必须用动态SQL来处理——先自动识别refcursor的结果集结构,再动态创建临时表,最后导入数据并添加序号:
CREATE OR REPLACE FUNCTION FN_TEST() RETURNS VOID LANGUAGE plpgsql AS $$ DECLARE V_CURS REFCURSOR; V_CURS_Rec RECORD; ITER INTEGER := 1; cols_info JSON; -- 存储结果集的列名称和类型 create_table_sql TEXT; insert_sql TEXT; BEGIN -- 获取目标refcursor SELECT * FROM FN_RET_REFCURSOR() INTO V_CURS; -- 从PostgreSQL系统表中获取refcursor对应的结果集元数据 SELECT json_agg(json_build_object('name', attname, 'type', format_type(atttypid, atttypmod))) FROM pg_attribute WHERE attrelid = ( SELECT pg_class.oid FROM pg_class JOIN pg_namespace ON pg_class.relnamespace = pg_namespace.oid WHERE relname = 'pg_result_' || substr(V_CURS::text, 2, strpos(V_CURS::text, ')')-2) ) AND attnum > 0 AND NOT attisdropped INTO cols_info; -- 动态生成临时表创建语句:包含序号列INDX + refcursor的所有列 create_table_sql := 'CREATE TEMPORARY TABLE IF NOT EXISTS TMP_TBL (INDX INTEGER NOT NULL, ' || string_agg(quote_ident(col->>'name') || ' ' || (col->>'type'), ', ') || ') ON COMMIT DROP'; -- 可选:会话结束自动删除临时表,避免残留 EXECUTE create_table_sql USING cols_info; -- 清空临时表(如果需要保留历史数据可删除此句) DELETE FROM TMP_TBL; -- 动态生成插入语句,处理record字段的展开逻辑 insert_sql := 'INSERT INTO TMP_TBL SELECT $1, ' || string_agg(quote_ident(col->>'name'), ', ') || ' FROM (SELECT ($2).*) AS t'; -- 循环读取refcursor数据并插入 LOOP FETCH V_CURS INTO V_CURS_Rec; EXIT WHEN NOT FOUND; EXECUTE insert_sql USING ITER, V_CURS_Rec; ITER := ITER + 1; END LOOP; RETURN; END; $$;
关键说明:
- 元数据获取:通过
pg_attribute系统表读取refcursor对应的临时结果集(PostgreSQL会自动为refcursor创建pg_result_xxx格式的临时表)的列信息,确保临时表结构和refcursor完全匹配。 - 动态SQL安全:用
quote_ident转义列名,避免特殊字符或关键字导致的SQL语法错误。 - 性能优化:如果数据量极大,循环fetch会比较慢,可以考虑改用
COPY或批量导入的方式,但复杂度会稍高。
内容的提问来源于stack exchange,提问作者Walentyna Juszkiewicz
相关产品推荐
相关产品推荐

