G-7130: Always use parameters or pull in definitions rather than referencing external variables in a local program unit.
Major
Maintainability, Reliability, Testability
Reason
Local procedures and functions offer an excellent way to avoid code redundancy and make your code more readable (and thus more maintainable). Your local program refers, however, an external data structure, i.e., a variable that is declared outside of the local program. Thus, it is acting as a global variable inside the program.
This external dependency is hidden, and may cause problems in the future. You should instead add a parameter to the parameter list of this program and pass the value through the list. This technique makes your program more reusable and avoids scoping problems, i.e. the program unit is less tied to particular variables in the program. In addition, unit encapsulation makes maintenance a lot easier and cheaper.
Example (bad)
CREATE OR REPLACE PACKAGE BODY EMPLOYEE_API IS
PROCEDURE calc_salary (in_employee_id IN employees.employee_id%TYPE) IS
r_emp employees%rowtype;
FUNCTION commission RETURN NUMBER IS
l_commission employees.salary%TYPE := 0;
BEGIN
IF r_emp.commission_pct IS NOT NULL
THEN
l_commission := r_emp.salary * r_emp.commission_pct;
END IF;
RETURN l_commission;
END commission;
BEGIN
SELECT *
INTO r_emp
FROM employees
WHERE employee_id = in_employee_id;
SYS.DBMS_OUTPUT.PUT_LINE(r_emp.salary + commission());
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
WHEN TOO_MANY_ROWS THEN
NULL;
END calc_salary;
END employee_api;
/
Example (good)
CREATE OR REPLACE PACKAGE BODY EMPLOYEE_API IS
PROCEDURE calc_salary (in_employee_id IN employees.employee_id%TYPE) IS
r_emp employees%rowtype;
FUNCTION commission (in_salary IN employees.salary%TYPE
,in_comm_pct IN employees.commission_pct%TYPE)
RETURN NUMBER IS
l_commission employees.salary%TYPE := 0;
BEGIN
IF in_comm_pct IS NOT NULL THEN
l_commission := in_salary * in_comm_pct;
END IF;
RETURN l_commission;
END commission;
BEGIN
SELECT *
INTO r_emp
FROM employees
WHERE employee_id = in_employee_id;
SYS.DBMS_OUTPUT.PUT_LINE(
r_emp.salary + commission(in_salary => r_emp.salary
,in_comm_pct => r_emp.commission_pct)
);
EXCEPTION
WHEN NO_DATA_FOUND THEN
NULL;
WHEN TOO_MANY_ROWS THEN
NULL;
END calc_salary;
END employee_api;
/