Thursday 8 September 2011

Function or Procedure

Finally, to reinforce my recommendation regarding functions vs. procedures: if your subprogram needs to perform one or more operations that do not directly contribute to finding, building and returning your data, you should do one of two things:
1. Define this subprogram as a procedure with OUT arguments.
2. Break up this subprogram into two subprograms: a function that does nothing but return the data and a procedure that executes a series of operations
For example, suppose my "get salary and hire date" program also generates an analysis of raises this person has received in the past and how it compares to others in her department. The analysis is then written out to a database table. As a function it might look like this (note: I am leaving off exception handling to keep the example simple, but you should certainly include error handling in your own production code!):
CREATE OR REPLACE PACKAGE BODY emp_lookups
IS
FUNCTION emp_sal_and_hd (
employee_id_in IN employees.employee_id%TYPE )
RETURN sal_and_hd_rt
IS
l_return sal_and_hd_rt;
l_analysis analysis_pkg.dataset_aat;
BEGIN
SELECT salary
, hire_date
INTO l_return
FROM employees
WHERE employee_id = employee_id_in;

l_analysis := analysis_pkg.salary_review ( employee_id_in );

INSERT INTO emp_analysis
( employee_id, analysis_type, dataset )
VALUES
( employee_id_in, analysis_pkg.c_salary_review, l_analysis );

RETURN l_return;
END emp_sal_and_hd;
END emp_lookups;
The fundamental problem with subprograms like this one is that the header of the function does not fully describe what it is doing. In addition, if I only want to get the salary and hire date and skip the analysis, I cannot use this function at all. My code would be much more flexible and reusable if I disentangled these two areas of functionality, into something like this:
CREATE OR REPLACE PACKAGE BODY emp_lookups
IS
FUNCTION emp_sal_and_hd (
employee_id_in IN employees.employee_id%TYPE )
RETURN sal_and_hd_rt
IS
l_return sal_and_hd_rt;
BEGIN
SELECT salary
, hire_date
INTO l_return
FROM employees
WHERE employee_id = employee_id_in;

RETURN l_return;
END emp_sal_and_hd;

PROCEDURE refresh_salary_review (
employee_id_in IN employees.employee_id%TYPE )
IS
l_analysis analysis_pkg.dataset_aat;
BEGIN
l_analysis := analysis_pkg.salary_review ( employee_id_in );

INSERT INTO emp_analysis
( employee_id, analysis_type, dataset )
VALUES
( employee_id_in, analysis_pkg.c_salary_review, l_analysis );

END refresh_salary_review;
END emp_lookups;

No comments:

Post a Comment