☰
Oracle 存储过程 sys_refcursor OUT 参数传参:返回 select 查询结果集的完整配置与验证
2026/10/2 12:09:47 网站建设 项目流程

1. 从一次取数失败说起:sys_refcursor OUT 参数到底怎么传

如果你写过 Oracle 存储过程,大概率遇到过这种需求:过程内部执行一段select,把结果集整个“抛”给调用方,而不是只返回一个标量值。这时候sys_refcursor作为 OUT 参数就是最标准的做法。它本质是一个游标类型的引用,过程里open ... for select ...,调用方拿到游标后自己fetch循环取值。

我见过太多人卡在第一步:过程建好了,调用脚本也写了,结果要么报ORA-06550,要么ORA-01000游标超限,要么客户端里根本看不到结果集。问题往往不在 SQL 本身,而在“传参链路”没打通——声明、绑定、取值、关闭,四个环节任何一个出错都会让结果集拿不到。

这篇就围绕oracle 存储过程 sys_refcursor out 参数 返回 select 查询结果集这条链路,给你一套可以直接复制运行的完整配置。从 PL/SQL 声明、绑定变量、客户端取值,到常见 ORA 报错排查,每一步都配上可执行代码和预期输出。最后我会说明怎么把数据库连接 endpoint 统一改到 TaoToken 的 API 通道,用同一套 Key 复测取数,方便你在多环境之间切换验证。

适合谁看:正在写 Oracle 存储过程、需要返回结果集的后端开发;用 PL/SQL Developer、SQL Developer、Navicat 或 Java/Python 客户端调用过程的同学;以及被sys_refcursor传参和 ORA 报错折腾过的运维。你不需要是 DBA,只要能连上库、能执行脚本,就能跟着走完。

先说结论:sys_refcursor是 OUT 参数,调用方必须先声明一个该类型的变量,把它作为实参传进去,过程内部open之后,调用方才能fetch。整个过程不需要return,结果集是通过这个“游标句柄”传出来的。理解这一点,后面所有代码都是它的展开。

2. 前置准备:TaoToken 统一 Key 与 API 通道配置

在写存储过程之前,先把连接通道理清楚。很多取数失败其实不是 PL/SQL 的问题,而是连接 endpoint 指向混乱——开发库、测试库、AI 辅助通道各用一套 Key,排查时根本分不清是哪一层出的错。我的做法是把数据库连接和 AI 辅助调用的 endpoint 统一收敛到 TaoToken,用同一套 Key 管理,复测时只改一个地址就能切换。

TaoToken 官网入口是 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= ,API 基址是 https://taotoken.net/api (这个地址不加 UTM 参数,直接用于程序配置)。它的作用是给你一个统一的 Key 和 API 通道,把模型对话、编码辅助、控制台管理这些入口集中起来,避免到处散落密钥。

具体操作上,你需要先拿到 Key。进入控制台页面 https://taotoken.net/console?utm_source=taotoken_aicg_blog_end&utm_content=console&utm_campaign=rewrite ,在 API Keys 管理页 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite 创建一个新 Key。创建时建议按用途命名,比如oracle-refcursor-test,方便后面复测时区分。Key 只显示一次,复制后存到安全的地方。

拿到 Key 之后,配置连接信息。以常见的 OpenAI 兼容客户端为例,Base URL 填https://taotoken.net/api,API Key 填你刚创建的那串,Model ID 按你实际要用的模型填。这三件套(Base URL + Key + Model ID)是后面所有验证的基础,缺一个都会报 401。

如果你用的是 Claude Code 这类编码工具,接入方式类似,Base URL 同样是https://taotoken.net/api,Key 用同一套。文档入口在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有各客户端的详细配置示例。需要长期跑编码或 Agent 任务的,可以看 Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite ,它适合持续性的开发场景。

这里要强调一点:TaoToken 是统一的 API 通道,不是让你绕过数据库本身。Oracle 存储过程的执行还是在你自己的数据库连接上,TaoToken 负责的是 AI 辅助调用这一层。两者分开配置、分开验证,排查时才能定位到具体是哪一层的问题。我试过把两层混在一起调,结果一个 401 查了半天,最后发现是数据库密码过期,跟 API Key 没关系。

配置完成后,先用模型对话页面 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite 发一条测试消息,确认 Key 和通道是通的。这一步过了,再进入下面的存储过程环节。

3. 可复制配置:存储过程定义与调用脚本

现在进入核心部分。先建一张测试表,再写存储过程,最后写调用脚本。所有代码都可以直接复制执行。

先准备测试数据:

-- 创建测试表 create table tb_user ( u_id number primary key, u_username varchar2(50), u_status number default 1 ); -- 插入几条测试数据 insert into tb_user values (1, 'admin', 1); insert into tb_user values (2, 'kaoshiyuan', 1); insert into tb_user values (3, 'tester', 0); commit;

接着定义存储过程。注意p_cur是out sys_refcursor,过程内部用open ... for打开它:

create or replace procedure p_test( p_cur out sys_refcursor ) is begin open p_cur for select u_id, u_username, u_status from tb_user where u_status = 1 order by u_id; end p_test; /

这个定义里,sys_refcursor是弱类型游标,不需要预先声明返回结构,open ... for select时动态绑定结果集。where u_status = 1只是示例过滤条件,你可以按实际业务改。

然后是匿名块调用。关键点:先声明一个sys_refcursor变量,把它作为实参传给过程,再fetch循环:

declare p_cur sys_refcursor; v_id tb_user.u_id%type; v_username tb_user.u_username%type; v_status tb_user.u_status%type; begin p_test(p_cur); loop fetch p_cur into v_id, v_username, v_status; exit when p_cur%notfound; dbms_output.put_line('ID:' || v_id || ' 用户名:' || v_username || ' 状态:' || v_status); end loop; close p_cur; end; /

执行前记得打开输出:set serveroutput on;。预期输出是:

ID:1 用户名:admin 状态:1 ID:2 用户名:kaoshiyuan 状态:1

如果你用%rowtype写法,也可以这样:

declare p_cur sys_refcursor; r tb_user%rowtype; begin p_test(p_cur); loop fetch p_cur into r; exit when p_cur%notfound; dbms_output.put_line('用户名:' || r.u_username); end loop; close p_cur; end; /

两种写法等价,%rowtype更省事,但要求结果集列顺序和表结构一致。如果过程里select的列和表不完全对应,用显式变量更安全。

对于 Java 客户端,调用方式是通过CallableStatement注册 OUT 参数:

CallableStatement cs = conn.prepareCall("{call p_test(?)}"); cs.registerOutParameter(1, OracleTypes.CURSOR); cs.execute(); ResultSet rs = (ResultSet) cs.getObject(1); while (rs.next()) { System.out.println(rs.getString("u_username")); } rs.close(); cs.close();

Python 用cx_Oracle或oracledb也类似,通过cursor.var(oracledb.CURSOR)声明 OUT 变量。核心逻辑一致:声明游标变量 → 传参 → 取结果集 → 关闭。

配置层面,如果你要把连接 endpoint 改到 TaoToken 统一通道,以 OpenAI 兼容客户端为例,配置文件片段如下:

{ "base_url": "https://taotoken.net/api", "api_key": "sk-你的Key", "model": "你的ModelID" }

如果是 TOML 格式(比如某些 CLI 工具):

[provider] base_url = "https://taotoken.net/api" api_key = "sk-你的Key" model = "你的ModelID"

Claude Code 的 settings 片段:

{ "env": { "ANTHROPIC_BASE_URL": "https://taotoken.net/api", "ANTHROPIC_API_KEY": "sk-你的Key" } }

注意:数据库连接串和这个 API 配置是两回事。数据库还是连你自己的 Oracle 实例,TaoToken 只管 AI 辅助那一层。复测取数时,先确认数据库连接正常,再确认 API 通道正常,分开验证。

4. 验证请求与成功结果:从执行到取数的完整链路

配置写完了,接下来是验证。验证分三层:存储过程本身能编译、匿名块能取到数据、客户端能拿到结果集。每一层都有明确的成功标志。

第一层,编译存储过程。执行create or replace procedure后,如果输出Procedure created.,说明语法没问题。如果报ORA-00900或ORA-06550,多半是open ... for后面的 SQL 有问题,单独把那段select拿出来跑一遍就能定位。

第二层,匿名块取数。执行第 3 节的调用脚本,成功标志是dbms_output输出三行用户数据。如果输出为空但没报错,检查两点:set serveroutput on是否执行;where条件是否把数据全过滤掉了。

第三层,客户端取值。以 Java 为例,rs.next()返回 true 且能打印出u_username,说明 OUT 参数传递成功。如果getObject(1)返回 null,检查registerOutParameter是否在execute之前调用。

我实测下来,最稳的验证顺序是:先在 SQL 客户端(比如 SQL Developer)里跑通匿名块,确认存储过程逻辑没问题;再写 Java/Python 调用,确认驱动和参数注册没问题;最后把 API 通道配置加上,确认 AI 辅助层没问题。三层分开,出问题时能快速定位。

一个完整的验证脚本,把建表、建过程、调用串起来:

-- 1. 建表 create table tb_user ( u_id number primary key, u_username varchar2(50), u_status number default 1 ); -- 2. 插数据 insert into tb_user values (1, 'admin', 1); insert into tb_user values (2, 'kaoshiyuan', 1); commit; -- 3. 建过程 create or replace procedure p_test(p_cur out sys_refcursor) is begin open p_cur for select u_id, u_username, u_status from tb_user where u_status = 1 order by u_id; end p_test; / -- 4. 调用验证 set serveroutput on; declare p_cur sys_refcursor; r tb_user%rowtype; begin p_test(p_cur); loop fetch p_cur into r; exit when p_cur%notfound; dbms_output.put_line('用户名:' || r.u_username); end loop; close p_cur; end; /

预期输出:

用户名:admin 用户名:kaoshiyuan

看到这两行,说明sys_refcursorOUT 参数传参链路完全打通。接下来可以把这个模式套到你的实际业务 SQL 上。

如果你在验证过程中用 TaoToken 的模型对话页面辅助排查,入口是 https://taotoken.net/chat?utm_source=taotoken_aicg_blog_end&utm_content=chat&utm_campaign=rewrite ,把报错信息贴进去让它帮你分析,比翻文档快。但记住,最终验证还是要在数据库里跑。

5. 常见报错排查:ORA 错误与连接问题对照

这一节把最常见的报错列出来,对照排查。每个报错都给出原因和解决方向。

ORA-06550 / PLS-00306:参数类型或数量不匹配。原因通常是调用时实参类型和过程定义的形参类型不一致。sys_refcursor必须用同类型变量接收,不能传字符串或数字。检查declare里声明的变量类型是不是sys_refcursor。

ORA-01000:超出打开游标的最大数。原因是没有close p_cur。每次调用过程都会打开一个游标,不关闭就会累积,最终超过open_cursors参数限制。解决:在fetch循环结束后务必close p_cur。如果客户端调用,也要在finally块里关闭ResultSet和CallableStatement。

ORA-01001:无效的游标。原因通常是游标已经关闭后又去fetch,或者过程内部没有成功open。检查过程里open ... for是否执行到,以及调用方是否在fetch前就关闭了游标。

ORA-00932:数据类型不一致。常见于fetch ... into时变量类型和结果集列类型不匹配。用%type或%rowtype声明变量可以避免大部分这类问题。

ORA-01422:实际返回的行数超出请求的行数。这个通常出现在select into场景,不是sys_refcursor的问题。如果你在过程里混用了select into和open for,检查select into是否只返回一行。

401 Unauthorized(API 层)。如果你在调用 TaoToken 通道时遇到 401,检查三件套:Base URL 是不是https://taotoken.net/api,Key 是不是完整复制(没有多余空格),Model ID 是不是填了有效的模型名。三个都对还报 401,去控制台确认 Key 是否被禁用或过期。

local proxy failed / connection refused。这类错误通常是本地网络或代理配置问题。检查你的客户端是否配置了额外的代理,或者 Base URL 写成了localhost。TaoToken 的地址是公网地址,不需要本地代理。

reading choices 报错。这通常出现在流式响应解析时,返回结构不符合预期。检查 Model ID 是否支持你调用的接口格式,以及请求体是否符合 OpenAI 兼容规范。

OAuth 相关报错。如果你用的是需要 OAuth 的客户端,检查 token 是否过期,以及回调地址是否配置正确。TaoToken 的 API Key 方式不需要 OAuth,直接用 Key 即可。

排查顺序建议:先看数据库层报错(ORA 开头),再看 API 层报错(HTTP 状态码),最后看客户端解析报错。分层定位,不要混在一起猜。

一个实用技巧:把open_cursors参数查出来看看当前值:

show parameter open_cursors;

如果值偏小(比如默认 300),在高并发调用存储过程时容易触发 ORA-01000。可以让 DBA 适当调大,但根本解决还是及时关闭游标。

6. 把连接 endpoint 改到 TaoToken 后复测取数

最后一步,把连接 endpoint 统一改到 TaoToken 通道,复测整个取数链路。这一步的目的是验证:当 AI 辅助层和数据库层分开配置后,取数逻辑是否依然稳定。

操作上,先确认数据库连接串没变,还是连你自己的 Oracle 实例。然后修改 AI 辅助客户端的配置,把 Base URL 改成https://taotoken.net/api,Key 用控制台创建的那串,Model ID 按实际填。三件套配置好后,重新跑一遍第 4 节的验证脚本。

复测时重点看两个地方:一是存储过程调用是否还返回同样的结果集,二是 API 通道是否正常响应。如果结果集一致,说明数据库层没问题;如果 API 通道也正常,说明整体链路打通。

如果你需要长期跑编码或 Agent 任务,建议用 Coding Plan 页面 https://taotoken.net/coding-plan?utm_source=taotoken_aicg_blog_end&utm_content=coding-plan&utm_campaign=rewrite 的配置方式,它适合持续性的开发场景。接入文档在 https://taotoken.net/doc?utm_source=taotoken_aicg_blog_end&utm_content=doc&utm_campaign=rewrite ,里面有各客户端的详细示例。API Keys 管理在 https://taotoken.net/api-keys?utm_source=taotoken_aicg_blog_end&utm_content=api-keys&utm_campaign=rewrite ,需要新建或轮换 Key 时去这里。

复测通过后,你可以把这个模式固化下来:数据库连接一套配置,AI 辅助通道一套配置,两者通过统一的 Key 管理。以后换环境或换模型,只改 API 配置,不动数据库脚本,排查时也更容易定位问题。

最后留一个实用技巧:把存储过程的调用脚本存成.sql文件,每次复测直接@文件名执行,避免手敲出错。对于sys_refcursor这种 OUT 参数,脚本里把declare、fetch、close写完整,比在客户端里临时拼 SQL 可靠得多。取数结果稳定后,再考虑把它封装成定时任务或接口,那是下一步的事了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询