Oracle显式游标参数必须声明为IN模式,不支持OUT或IN OUT;所有参数仅用于传入值,不能返回数据,且需在WHERE中用“p_col IS NULL OR col = p_col”处理NULL逻辑。

游标参数必须声明为 IN,不能用 OUT 或 IN OUT
Oracle 的显式游标只支持 IN 类型的参数,这是硬性限制。如果你在定义游标时写了 OUT 或 IN OUT,PL/SQL 编译器会直接报错:PLS-00103: Encountered the symbol "OUT"。这意味着所有参数只能传入值,不能通过游标参数“返回”数据——想输出得靠 FETCH 后的变量或函数返回。
实操建议:
- 游标定义中所有参数都显式写成
IN(即使省略也默认是IN,但写出来更清晰) - 避免在游标体里尝试修改参数值,它只是只读副本
- 若需动态控制“是否启用某条件”,用
NULL作占位符,再在WHERE子句中用NVL或COALESCE判断
WHERE 条件要用 NVL 或 CASE 避免参数为 NULL 时逻辑失效
直接写 WHERE dept_id = p_dept_id 看似简洁,但当调用时传入 NULL(表示“不限部门”),整行记录就全被过滤掉了——因为 col = NULL 永远为 FALSE。必须把“参数为空则跳过该条件”这个语义显式写进 SQL。
推荐写法(以两参数为例):
DECLARE
CURSOR emp_cur(p_dept_id IN NUMBER, p_status IN VARCHAR2) IS
SELECT emp_id, emp_name, dept_id, status
FROM employees
WHERE (p_dept_id IS NULL OR dept_id = p_dept_id)
AND (p_status IS NULL OR status = p_status);
注意点:
- 别用
NVL(p_dept_id, dept_id) = dept_id,看似等价,但会导致全表扫描(无法走dept_id索引) - 如果字段允许
NULL值且业务上要区分“未传参”和“传了 NULL”,就得额外加一个标志参数,比如p_dept_id_is_used IN BOOLEAN(但 Oracle 游标不支持布尔参数,得改用NUMBER(1)或VARCHAR2(1)) - 条件越多,括号嵌套越容易出错,建议每个可选条件单独一行并缩进对齐
OPEN 时传参顺序必须与游标声明严格一致
Oracle 不支持按名称传参(如 OPEN emp_cur(p_status => 'A')),只支持按位置。一旦顺序错位,比如把 status 当成 dept_id 传,查询结果就完全不可控,而且不会报错——静默出错最危险。
安全做法:
- 声明游标参数时,把最常变化、最可能为
NULL的参数放在前面,固定值放后面,降低调用时错位概率 - 在
OPEN前用局部变量接收参数值,并加注释说明含义,例如:l_dept_id := 10; -- HR 部门 - 如果参数多于 3 个,考虑封装成记录类型(
RECORD)或改用 ref cursor + 动态 SQL,反而更易维护
REF CURSOR 比静态游标更适合复杂复用场景
当条件组合爆炸(比如 5 个可选字段,有 2⁵=32 种组合),硬编码所有 OR 分支会让 SQL 可读性归零,优化器也可能生成次优执行计划。这时该换思路:用 SYS_REFCURSOR 配合动态 SQL。
示例关键片段:
PROCEDURE open_emp_rc(
p_dept_id IN NUMBER,
p_status IN VARCHAR2,
p_rc OUT SYS_REFCURSOR
) IS
l_sql VARCHAR2(2000) := 'SELECT emp_id, emp_name FROM employees WHERE 1=1';
BEGIN
IF p_dept_id IS NOT NULL THEN
l_sql := l_sql || ' AND dept_id = :1';
END IF;
IF p_status IS NOT NULL THEN
l_sql := l_sql || ' AND status = :2';
END IF;
OPEN p_rc FOR l_sql USING p_dept_id, p_status;
END;注意:
- 动态拼接时务必用
USING绑定变量,杜绝字符串拼接值(防 SQL 注入+日期格式错误) -
SYS_REFCURSOR不能直接FETCH,必须先OPEN再在调用方处理,上下文分离更明显 - 动态 SQL 的执行计划每次可能不同,高并发下要注意共享池压力;必要时用
DBMS_SQL手动绑定+缓存,但复杂度陡增
真正难的不是语法,而是判断什么时候该停手——把 7 个参数塞进一个静态游标,不如拆成 2 个语义清晰的游标,或者直接上动态 SQL。边界感比技巧更重要。


















