ref cursor 是指向结果集的指针,须先声明再用 open ... for 打开,不可直接赋值sql字符串;动态sql需用using传参防注入,强类型声明更安全;其作用域限于当前会话,不可跨会话或自治事务传递,且必须由调用方显式关闭以防内存泄漏。
ref cursor 变量必须先声明再打开,不能直接赋值查询字符串
很多人以为 ref cursor 像普通变量一样能直接接 sql 字符串,比如写成 my_cursor := 'select * from emp'; —— 这会报 pls-00382: expression is of wrong type。它本质是「指向结果集的指针」,不是容器,不存 sql 文本。
正确做法是用 OPEN ... FOR 语法绑定动态 SQL:
DECLARE TYPE emp_cur_type IS REF CURSOR; emp_cur emp_cur_type; v_deptno NUMBER := 10; BEGIN OPEN emp_cur FOR 'SELECT ename, sal FROM emp WHERE deptno = :1' USING v_deptno; -- 后续 FETCH ... END;
- 动态 SQL 必须用字符串字面量或
VARCHAR2变量,不能拼接未转义的用户输入(防注入) -
USING子句传参比字符串拼接安全,且支持所有数据类型(包括对象、集合) - 如果 SQL 固定,优先用静态游标(
CURSOR c IS SELECT...),性能更好、编译期可校验
函数返回 REF CURSOR 时,类型必须显式声明为弱类型或强类型
想让函数把查询结果“吐出来”给调用方(比如 Java 的 CallableStatement),返回类型不能写 SYS_REFCURSOR 就完事——虽然 Oracle 允许,但强类型更稳,尤其涉及列名/类型校验时。
两种声明方式区别明显:
- 弱类型:
TYPE gen_refcur IS REF CURSOR;→ 可打开任意查询,灵活但无结构约束 - 强类型:
TYPE emp_refcur IS REF CURSOR RETURN emp%ROWTYPE;→ 打开的查询必须严格匹配emp表结构,否则编译报错PLS-00386: type mismatch in function return value
示例函数(强类型):
CREATE OR REPLACE FUNCTION get_emps_by_dept(p_deptno NUMBER) RETURN emp_refcur IS l_cur emp_refcur; BEGIN OPEN l_cur FOR SELECT * FROM emp WHERE deptno = p_deptno; RETURN l_cur; -- 注意:不能 CLOSE,交给调用方处理 END;
调用时务必记得 FETCH 和 CLOSE,否则游标长期占用 PGA 内存。
动态 SQL 中绑定变量个数与 USING 顺序必须严格一致
写 OPEN cur FOR 'SELECT ... WHERE a = :1 AND b = :2' USING x, y; 看似简单,但实际容易翻车:
- 占位符
:1,:2是位置序号,不是名字,:x这种命名写法在动态 SQL 里无效 -
USING后的参数个数少于占位符数 → 报ORA-01008: not all variables bound - 多于占位符数 → 多余参数被忽略,不报错但逻辑可能错(比如本该查 dept=10,却因参数错位查了 dept=20)
- 参数类型不匹配(如把
DATE当NUMBER传)→ 运行时报ORA-06502或隐式转换失败
建议:动态 SQL 超过 2 个绑定变量时,改用 DBMS_SQL(更可控)或重构为静态 + 条件分支。
REF CURSOR 不能跨会话传递,也不能在自治事务中返回给主事务
有人想把游标从一个存储过程传到另一个,甚至塞进全局临时表——不行。REF CURSOR 是会话级内存地址,只在当前 session 的 PGA 里有效。一旦 session 断开,指针就悬空,FETCH 会报 ORA-01001: invalid cursor。
常见误用场景:
- 在自治事务(
PRAGMA AUTONOMOUS_TRANSACTION)里打开游标并返回 → 主事务拿到的是已关闭或无效的游标 - 试图把
REF CURSOR作为参数传给远程数据库链路(DB Link)上的过程 → 不支持 - 在包变量里缓存
REF CURSOR并复用 → 每次必须重新OPEN,否则报ORA-01001
替代方案:需要共享结果集,改用全局临时表(GTTS)或集合类型(TABLE OF);需要跨会话,走中间表或消息队列。
最常被忽略的一点:REF CURSOR 的生命周期完全由打开它的 PL/SQL 块控制,调用方不 CLOSE,它就一直占着内存,直到会话结束。高并发下容易触发 ORA-01000: maximum open cursors exceeded。










