Oracle存储过程参数传递必须严格按IN、OUT、IN OUT类型声明并使用:IN参数只读,误赋值报PLS-00363;OUT参数调用前须声明且初始值被忽略;IN OUT仅适用于输入值参与计算并返回修改结果的场景,滥用会降低可读性。
oracle 存储过程的参数传递不是“选哪种方便”,而是必须按类型明确声明、按语义严格使用。in out 参数确实能减少变量声明和赋值次数,但它的效率提升只在特定场景成立——不是所有双向传参都该用 in out。
IN 参数被意外修改会直接报错 PLS-00363
IN 参数本质是只读副本,哪怕你没写 IN 关键字(默认就是 IN),也不能在过程体内给它重新赋值:
CREATE OR REPLACE PROCEDURE bad_proc(p_id NUMBER) IS BEGIN p_id := p_id + 1; -- 这行触发 PLS-00363 错误 END;
- 错误信息固定为
PLS-00363: expression 'P_ID' cannot be used as an assignment target - 常见于从其他语言(如 Java)直译逻辑时,误把输入参数当本地变量用
- 修复方式只有两种:改用
IN OUT(如果真需修改),或声明新变量承接计算结果
OUT 参数调用前必须声明变量,且初始值会被忽略
OUT 参数不接收调用方传入的值,即使你提前赋了值,进过程后仍是 NULL:
DECLARE v_name VARCHAR2(20) := 'old_value'; BEGIN example_proc(100, v_name); -- v_name 进入过程后是 NULL,不是 'old_value' DBMS_OUTPUT.PUT_LINE(v_name); -- 输出的是过程内赋的值,或仍为 NULL END;
- 调用含
OUT的过程前,VARIABLE命令(SQL*Plus)或DECLARE块(PL/SQL)必不可少 -
EXEC example_proc(100, :v_name)中的:v_name必须已用VARIABLE v_name VARCHAR2(20)声明过 - 别指望靠预设初始值做“默认 fallback”,OUT 就是清空重来
IN OUT 参数真正省事的场景只有一个
IN OUT 的价值不在“既能输又能出”,而在于“输入值参与计算,且输出结果依赖该输入的原始状态”。比如字符串拼接、数值累加、状态标记翻转:
CREATE OR REPLACE PROCEDURE append_log(p_msg IN OUT VARCHAR2, p_suffix VARCHAR2) IS BEGIN p_msg := p_msg || ' | ' || p_suffix; -- 输入值被读取,再被覆盖写回 END;
- 调用时可传非空字符串:
p_msg := 'start'; append_log(p_msg, 'done');→p_msg变成'start | done' - 若用两个独立参数(
IN+OUT),就得额外声明中间变量,多一次赋值 - 但注意:
NOCOPY对 IN OUT 有实际意义——加在参数声明后可避免大对象拷贝:p_data IN OUT NOCOPY SYS_REFCURSOR - 滥用 IN OUT 的典型反例:只是想返回一个新值,却硬把输入参数当容器用,反而让逻辑难懂
参数传递方式不影响执行权限,但影响调试可见性
存储过程始终以定义者权限运行,和参数类型无关。但参数类型决定了你在调试时能看到什么:
- IN 参数值可在
DBMS_OUTPUT.PUT_LINE中直接打印(过程内可见) - OUT / IN OUT 参数在过程结束前无法确认是否被赋值,容易漏掉
NULL情况导致调用方空指针类错误 - 调试时建议对每个 OUT 参数加判空:
IF p_result IS NULL THEN ...,尤其涉及查询未命中时 - 别依赖 SQL*Plus 的
PRINT自动格式化——它对日期、数字精度可能截断,用DBMS_OUTPUT更可控


















