Oracle PL/SQL包中重载过程调用的参数匹配疑问
create or replace PACKAGE BODY emp_pkg IS FUNCTION valid_deptid(p_deptid IN departments.department_id%TYPE) RETURN BOOLEAN IS v_dummy PLS_INTEGER; BEGIN SELECT 1 INTO v_dummy FROM departments WHERE department_id = p_deptid; RETURN TRUE; EXCEPTION WHEN NO_DATA_FOUND THEN RETURN FALSE; END valid_deptid; PROCEDURE add_employee( ----PRIMER PROCEDURE p_first_name employees.first_name%TYPE, p_last_name employees.last_name%TYPE, p_email employees.email%TYPE, p_job employees.job_id%TYPE DEFAULT 'SA_REP', p_mgr employees.manager_id%TYPE DEFAULT 145, p_sal employees.salary%TYPE DEFAULT 1000, p_comm employees.commission_pct%TYPE DEFAULT 0, p_deptid employees.department_id%TYPE DEFAULT 30) IS BEGIN IF valid_deptid(p_deptid) THEN INSERT INTO employees(employee_id, first_name, last_name, email, job_id, manager_id, hire_date, salary, commission_pct, department_id) VALUES (employees_seq.NEXTVAL, p_first_name, p_last_name, p_email, p_job, p_mgr, TRUNC(SYSDATE), p_sal, p_comm, p_deptid); ELSE RAISE_APPLICATION_ERROR(-20204, 'Invalid department ID.'); END IF; END add_employee; PROCEDURE add_employee ( ---- SEGUNDO PROCEDURE, SOBRECARGADO p_first_name employees.first_name%TYPE, p_last_name employees.last_name%TYPE, p_deptid employees.department_id%TYPE) IS p_email employees.email%TYPE; BEGIN p_email := UPPER(SUBSTR(p_first_name, 1, 1)||SUBSTR(p_last_name, 1, 7)); add_employee(p_first_name, p_last_name, p_email, p_deptid => p_deptid); END; PROCEDURE get_employee( p_empid IN employees.employee_id%TYPE, p_sal OUT employees.salary%TYPE, p_job OUT employees.job_id%TYPE) IS BEGIN SELECT salary, job_id INTO p_sal, p_job FROM employees WHERE employee_id = p_empid; END get_employee; END emp_pkg;
该代码可正常运行,但我对3参数的重载add_employee过程中调用8参数add_employee的写法存在疑惑,调用代码如下:
add_employee(p_first_name, p_last_name, p_email, p_deptid => p_deptid);
调用行为细节:
- 前两个参数
p_first_name、p_last_name按位置匹配被调用过程的前两个参数,这部分清晰。 - 第三个
p_email是当前重载过程的本地变量,而非被调用过程的第三个参数。 - 最后使用
p_deptid => p_deptid指定参数,前者是被调用过程的形参,后者是当前过程的实参。
具体疑问:
- 为何Oracle将调用中的
p_email识别为当前过程的本地变量,而非被调用过程的同名参数?我将其改为v_email后仍可正常运行。 - 在调用语句中,能否混合使用被调用过程的参数与当前重载过程的本地变量?
- 为何必须使用
p_deptid => p_deptid指定参数?若仅写p_deptid,Oracle不会识别为被调用过程的对应参数吗?
疑问解答
1. 关于p_email的识别逻辑
PL/SQL遵循作用域优先级:当前过程的本地变量(包括声明的变量、参数)优先级高于外部(被调用过程)的同名标识符。在3参数的add_employee过程中,p_email是本地声明的变量,所以调用语句里的p_email会优先指向这个本地变量,而不是被调用过程的第三个参数。就算改成v_email,只要是当前过程的本地变量,Oracle都会优先识别它,这是作用域规则的正常表现。
2. 能否混合使用被调用过程参数与当前过程本地变量
完全可以。调用过程时,实参可以是任何合法的PL/SQL表达式,包括当前过程的本地变量、参数、常量,甚至是被调用过程的参数(如果能访问到的话)。只要实参的数据类型与被调用过程的形参匹配,就可以混合使用,PL/SQL会根据作用域规则解析每个标识符。
3. 为何必须用命名参数指定p_deptid
如果仅写p_deptid,按位置匹配的话,这个参数会对应被调用过程的第四个参数p_job,而不是最后一个p_deptid参数。被调用的8参数add_employee中,前三个是必填无默认值的参数,后面五个有默认值。当你传入前三个参数后,第四个位置对应的是p_job,而p_deptid是第八个位置的参数。如果直接传p_deptid,Oracle会把它当作第四个参数的值,但p_deptid是部门ID类型,和p_job的职位ID类型不匹配,会触发类型错误;就算类型碰巧兼容,逻辑上也会把部门ID赋值给职位参数,导致业务错误。
使用p_deptid => p_deptid是命名参数传递,可以跳过中间有默认值的参数,直接给目标形参赋值,避免位置匹配的错误,同时让代码更清晰,明确指定哪个参数对应哪个值。
内容的提问来源于stack exchange,提问作者sara montenegro abad

