dbms_sql.return_result 是隐式返回查询结果的过程,不能替代 ref cursor,而是补充:它无需 out sys_refcursor 参数,不返回游标句柄,客户端自动接收展示,调用端无法 fetch 或 close。

DBMS_SQL.RETURN_RESULT 是什么,能直接替代 REF CURSOR 吗
不能替代,它是补充。DBMS_SQL.RETURN_RESULT 不需要定义 OUT SYS_REFCURSOR 参数,也不依赖包头声明的自定义游标类型,而是把打开的游标“推”给客户端,由 SQL*Plus、SQL Developer 或支持隐式结果集的驱动(如 Oracle JDBC 12.2+)自动接收并展示。它不返回句柄,调用端无法再对游标做 FETCH 或 CLOSE —— 这是关键区别。
常见错误现象:PLS-00306: wrong number or types of arguments 出现在你试图像调用普通过程那样用 EXEC + 变量绑定去接收结果;或者在旧版 JDBC(
- 必须用
OPEN ... FOR显式打开游标变量(SYS_REFCURSOR类型),再传给DBMS_SQL.RETURN_RESULT - 一个过程内可多次调用
DBMS_SQL.RETURN_RESULT,每次输出一个独立结果集,客户端按顺序编号(ResultSet #1、#2…) - 不支持嵌套调用:不能在另一个
RETURN_RESULT调用内部再开游标并返回
怎么写一个带隐式结果集的存储过程
核心就三步:声明游标变量 → OPEN ... FOR 查询 → DBMS_SQL.RETURN_RESULT 推出。不需要包头、不需要 OUT 参数、不需要 REF CURSOR 类型定义。
示例(直接运行即可):
CREATE OR REPLACE PROCEDURE get_user_summary AS l_cur1 SYS_REFCURSOR; l_cur2 SYS_REFCURSOR; BEGIN OPEN l_cur1 FOR SELECT username, created FROM dba_users WHERE ROWNUM OPEN l_cur2 FOR SELECT COUNT(*) cnt FROM dba_users; DBMS_SQL.RETURN_RESULT(l_cur2); END; /
注意点:
-
l_cur1和l_cur2是局部变量,类型固定为SYS_REFCURSOR,不能用自定义类型(如pkg.cursorRef) - 查询中避免使用未授权对象或动态拼接(
EXECUTE IMMEDIATE),否则权限检查会在执行时失败,报ORA-00942 - 若过程里混用了传统
OUT SYS_REFCURSOR参数,隐式结果集仍会发出,但调用端需额外处理两种返回机制,容易混乱
SQL*Plus 和 SQL Developer 中怎么验证隐式结果
必须启用隐式结果集支持。SQL*Plus 默认关闭,SQL Developer 18.4+ 默认开启。
SQL*Plus 中要先执行:
SET SERVEROUTPUT OFF SET FEEDBACK OFF -- 关键:启用隐式结果 SET IMPLICIT ON
然后执行:
EXEC get_user_summary;
你会看到类似:
ResultSet #1 <p>USERNAME CREATED</p><hr><p>SYS 12-JUN-2025 SYSTEM 12-JUN-2025 ...</p><h2>ResultSet #2 CNT</h2><pre class="brush:php;toolbar:false;"> 37
容易踩的坑:
- 忘记
SET IMPLICIT ON:结果集完全不显示,只打印 “PL/SQL procedure successfully completed.” - 同时开了
SET SERVEROUTPUT ON:DBMS_OUTPUT 输出和隐式结果混排,字段对齐错乱 - 在匿名 PL/SQL 块里调用该过程(如
BEGIN get_user_summary; END;):隐式结果仍生效,但部分老版本 SQL*Plus 会丢第一个结果集
JDBC 调用隐式结果集要注意什么
Oracle JDBC 驱动必须 ≥ 12.2,且连接属性 oracle.jdbc.implicitResults=true(JDBC 12.2 默认为 false,19c+ 默认 true)。否则 execute() 返回 true,但 getResultSet() 拿不到第一个结果,必须靠循环 getMoreResults() 才能取到。
Java 示例关键片段:
Connection conn = DriverManager.getConnection(url, props);
// 确保 props 包含 "oracle.jdbc.implicitResults=true"
CallableStatement cs = conn.prepareCall("{call get_user_summary()}");
cs.execute();
<p>ResultSet rs1 = cs.getResultSet(); // 第一个隐式结果集
while (rs1.next()) { ... }</p><p>if (cs.getMoreResults()) { // 切到第二个
ResultSet rs2 = cs.getResultSet();
while (rs2.next()) { ... }
}</p>
性能与兼容性提醒:
- 隐式结果集不经过 PL/SQL 引擎的 OUT 参数通道,网络传输更轻,但无法控制游标生命周期(如提前
CLOSE) - 不支持在 PL/SQL 块中捕获隐式结果集内容 —— 你不能用
FETCH读它,也不能赋值给另一个游标变量 - 如果过程里既有
DBMS_SQL.RETURN_RESULT又有传统OUT SYS_REFCURSOR,JDBC 必须用registerOutParameter处理后者,而前者靠隐式机制,二者逻辑隔离
真正容易被忽略的是:隐式结果集的列元数据(比如 NULLABLE、SCALE)由查询语句决定,不继承自表定义;如果用 SELECT 1 AS flag FROM DUAL,flag 的 JDBC 类型是 INTEGER,不是 NUMBER,下游映射可能出错。











