SYS_REFCURSOR必须用作OUT参数动态返回结果集,因其是弱类型引用游标,可跨作用域传递并运行时打开任意查询;普通显式游标静态绑定、不可传出。

直接用 SYS_REFCURSOR 作为 OUT 参数,就能让存储过程返回结果集——不是把数据塞进临时表,而是把游标句柄交出去,调用方自己去 fetch。
为什么必须用 SYS_REFCURSOR 而不是普通游标变量
普通显式游标(比如 CURSOR c IS SELECT ...)是静态的、编译期绑定的,只能在声明它的 PL/SQL 块内使用,无法传出。而 SYS_REFCURSOR 是弱类型引用游标,本质是个指针,可以跨作用域传递,还能在运行时动态打开任意查询。
- 它不依赖具体查询结构,同一变量可先后
OPEN ... FOR SELECT * FROM t1和OPEN ... FOR SELECT x,y FROM t2 - 客户端(如 JDBC、PL/SQL Developer 的 Test Window)能识别并显示其结果集
- 不能直接
FETCH它——必须先OPEN,再由调用方处理
CREATE OR REPLACE PROCEDURE 中如何声明和打开 SYS_REFCURSOR
关键点:参数方向必须是 OUT,且在 BEGIN 块里用 OPEN ... FOR 赋值,不是 SELECT ... INTO。
- 声明格式固定:
p_result OUT SYS_REFCURSOR,不能写成IN OUT或漏掉OUT -
OPEN p_result FOR SELECT ...必须在BEGIN内,不能放在声明区 - 查询语句支持绑定变量、子查询、甚至动态 SQL(用
EXECUTE IMMEDIATE+OPEN ... FOR)
CREATE OR REPLACE PROCEDURE get_user_data(p_dept_id IN NUMBER, p_result OUT SYS_REFCURSOR) IS
BEGIN
OPEN p_result FOR
SELECT id, name, salary
FROM users
WHERE dept_id = p_dept_id
AND status = 'ACTIVE';
END;
调用时怎么拿到并遍历这个游标结果
不能在另一个存储过程中直接 FETCH ——那是对本地游标的用法。这里要分两步:先执行过程获取游标句柄,再用 FETCH 从该句柄读数据(通常在匿名块或客户端)。
- PL/SQL 匿名块调用示例中,必须用
:cur_var(带冒号)声明变量接收输出参数 - 如果用 SQL*Plus 或 SQL Developer,需提前执行
SET SERVEROUTPUT ON才能看到DBMS_OUTPUT输出 - JDBC 中对应的是
CallableStatement.registerOutParameter(?, Types.OTHER),再用getResultSet()
BEGIN get_user_data(p_dept_id => 10, p_result => :cur_out); END;
然后在工具的 Variables 面板里把 cur_out 类型设为 Cursor,点 Value 栏右侧图标即可展开结果。
容易踩的坑:没关游标、类型不匹配、空结果没处理
SYS_REFCURSOR 不像普通游标那样强制要求 CLOSE,但若在存储过程中反复打开而不关,可能耗尽会话游标数(open_cursors 参数限制);更常见的是调用方忘记处理空结果或异常分支。
- 存储过程里不要写
CLOSE p_result——这会让调用方拿到一个已关闭的句柄,fetch 时报ORA-01001: invalid cursor - 如果查询可能无数据,
OPEN ... FOR仍会成功,只是后续 fetch 立即遇到%NOTFOUND,需由调用方判断 - 参数类型错配(比如传
VARCHAR2给期望NUMBER的IN参数)会导致ORA-06502,不是游标问题,但常被误判
真正麻烦的是嵌套调用场景:A 过程调 B 过程获取游标,B 又调 C 过程——此时每个环节的游标生命周期必须理清,否则一环 close 就全崩。











