Oracle REF CURSORs
Below is an example of how to setup a REF CURSOR and how to call it from a PL/SQL block.
Why would you want to use REF CURSOR's? REF CURSORs can often provide greater performance when working with data. Much of these comes from the fact that packages are stored into the SHARED POOL of the Oracle database or otherwise known as PINNED to memory.
What I did find interesting was that the process that executes this procedure never needs to know what tables the view touches or even be able to see the view. The procedure simply returns the requested columns.
Also: It would be entirely feasible, using REF CURSOR design, to have queries stored in a LONG column in a table. Remembering that the length of the data cannot exceed 32K. These queries can then be retrieved at package execution time. This could often reduce the number of production moves regarding packages. You could further enhance it by versioning your queries, so that only the most recent would be retrieved.
Provided is an example of a FUNCTION and a PROCEDURE:
PL/SQL Block:
/* Formatted on 2003/01/27 13:22 (Formatter Plus v4.7.0) */
DECLARE
emp_num VARCHAR2 (2000) := '9769,28441';
emp_cur emp_refcur_pkg.c_cursor;
emp_rec hum_emp_career_hist_v%ROWTYPE;
l_counter PLS_INTEGER := 0;
l_start_time DATE;
l_end_time DATE;
l_elapsed_time NUMBER;
l_error NUMBER;
BEGIN
l_start_time := SYSDATE;
emp_refcur_pkg.employeecareer (emp_num, l_error, emp_cur);
IF l_error = 0
THEN
LOOP
FETCH emp_cur INTO emp_rec;
EXIT WHEN emp_cur%NOTFOUND;
l_counter := l_counter + 1;
DBMS_OUTPUT.put_line ('==============');
DBMS_OUTPUT.put_line (l_counter);
DBMS_OUTPUT.put_line ('==============');
DBMS_OUTPUT.put_line ('Employee Number := '
|| emp_rec.employee_number
);
DBMS_OUTPUT.put_line ('Employee Name := ' || emp_rec.employee_name);
DBMS_OUTPUT.put_line ( 'Eff Start Date := '
|| emp_rec.effective_start_date
);
DBMS_OUTPUT.put_line ( 'Eff End Date := '
|| emp_rec.effective_end_date
);
DBMS_OUTPUT.put_line ('Job Title := ' || emp_rec.job_title);
DBMS_OUTPUT.put_line ('Job Code := ' || emp_rec.job_code);
DBMS_OUTPUT.put_line ('COID := ' || emp_rec.coid);
DBMS_OUTPUT.put_line ('UDN := ' || emp_rec.udn);
DBMS_OUTPUT.put_line ('Organization := ' || emp_rec.NAME);
END LOOP;
emp_refcur_pkg.employeecareer_close (emp_cur);
ELSE
DBMS_OUTPUT.put_line ('Error Encountered.');
END IF;
IF l_counter = 0
THEN
DBMS_OUTPUT.put_line ('No Records to Process');
ELSE
DBMS_OUTPUT.put_line (' ');
DBMS_OUTPUT.put_line (' ');
DBMS_OUTPUT.put_line (l_counter || ' record(s) found.');
END IF;
l_end_time := SYSDATE;
l_elapsed_time := ROUND ((l_end_time - l_start_time) * 24 * 60 * 60, 2);
DBMS_OUTPUT.put_line (l_elapsed_time || ' second(s) execution time.');
EXCEPTION
WHEN OTHERS
THEN
hum_do.default_exception ( 'ERROR :: '
|| 'Employee Number := '
|| emp_rec.employee_number
|| 'Employee Name := '
|| emp_rec.employee_name
|| 'Eff Start Date := '
|| emp_rec.effective_start_date
|| 'Eff End Date := '
|| emp_rec.effective_end_date
);
emp_refcur_pkg.employeecareer_close (emp_cur);
END;
RESULT
============== 1 ============== Employee Number := 9769 Employee Name := Chase, Mr. Barry S Eff Start Date := 16-OCT-2000 Eff End Date := 30-MAY-2002 Job Title := Consulting Systems Engineer Job Code := JE9 COID := 11701 UDN := 920 Organization := 11701/920 Applications Development 7 ============== 2 ============== Employee Number := 28441 Employee Name := Johnson, Ms. Faye M Eff Start Date := 30-DEC-2001 Eff End Date := 31-DEC-4712 Job Title := Systems Engineer Ii Job Code := PF9 COID := 11701 UDN := 920 Organization := 11701/920 Applications Development 7 ============== 3 ============== Employee Number := 28441 Employee Name := Johnson, Ms. Faye M Eff Start Date := 31-DEC-2000 Eff End Date := 31-DEC-2000 Job Title := Systems Engineer I Job Code := PF7 COID := 11701 UDN := 908 Organization := 11701/908 IT Education ============== 4 ============== Employee Number := 28441 Employee Name := Johnson, Ms. Faye M Eff Start Date := 26-JUN-2000 Eff End Date := 30-DEC-2000 Job Title := Associate Systems Engineer Job Code := RW6 COID := 11701 UDN := 908 Organization := 11701/908 IT Education 4 record(s) found.
VIEW
-- Displays Career History in Desc order where most current job history is first rec returned
-- Ordered by FULL_NAME, EMPLOYEE_NUMBER, EFFECTIVE_START_DATE DESC
CREATE OR REPLACE VIEW APPS.HUM_EMP_CAREER_HIST_V
AS /* Formatted on 2003/01/22 12:08 (Formatter Plus v4.7.0) */
SELECT ppf.employee_number, ppf.full_name employee_name
, paf.effective_start_date, paf.effective_end_date
, pjd.segment1 job_title, pjd.segment2 job_code, pca.segment1 coid
, pca.segment2 udn, hou.NAME
FROM apps.per_all_people_f ppf
, hr.per_person_type_usages_f ptu
, hr.per_person_types ppt
, hr.per_jobs pj
, hr.per_job_definitions pjd
, hr.per_periods_of_service pps
, hr.hr_all_organization_units hou
, hr.pay_cost_allocation_keyflex pca
, hr.per_jobs pjp
, hr.per_job_definitions pjdp
, apps.hr_organization_units houp
, hr.pay_cost_allocation_keyflex pcap
, apps.per_all_assignments_f pafp
, apps.per_all_assignments_f paf
WHERE pcap.cost_allocation_keyflex_id(+) = houp.cost_allocation_keyflex_id
AND (pjd.segment1 pjdp.segment1 OR pjdp.segment1 IS NULL)
AND pjdp.job_definition_id(+) = pjp.job_definition_id
AND houp.organization_id(+) = pafp.organization_id
AND pjp.job_id(+) = pafp.job_id
AND pafp.assignment_type(+) = 'E'
AND pafp.primary_flag(+) = 'Y'
AND pafp.effective_end_date(+) < TO_DATE ('31-DEC-4712', 'DD-MON-YYYY')
AND pafp.effective_end_date(+) + 1 = paf.effective_start_date
AND pafp.assignment_id(+) = paf.assignment_id
AND pca.cost_allocation_keyflex_id = hou.cost_allocation_keyflex_id
AND pjd.job_definition_id = pj.job_definition_id
AND ppt.system_person_type IN ('EMP', 'EX_EMP')
AND ppt.person_type_id = ptu.person_type_id
AND paf.effective_start_date BETWEEN ptu.effective_start_date
AND ptu.effective_end_date
AND ptu.person_id = paf.person_id
AND hou.organization_id = paf.organization_id
AND pj.job_id = paf.job_id
AND pps.period_of_service_id = paf.period_of_service_id
AND TRUNC (SYSDATE) BETWEEN ppf.effective_start_date
AND ppf.effective_end_date
AND ppf.person_id = paf.person_id
AND paf.assignment_type = 'E'
AND paf.primary_flag = 'Y'
ORDER BY ppf.full_name, ppf.employee_number, paf.effective_start_date desc
PACKAGE
/* Formatted on 2003/01/27 13:12 (Formatter Plus v4.7.0) */
CREATE OR REPLACE PACKAGE emp_refcur_pkg
AS
TYPE c_cursor IS REF CURSOR;
/* return Career History for given employee */
PROCEDURE employeecareer (
p_employee_number IN VARCHAR2
, p_error OUT NUMBER
, emp_cur OUT c_cursor
);
/* return org tree with level variable */
PROCEDURE employeeorg_vchild (
p_employee_number IN VARCHAR2
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
, p_level IN NUMBER
, p_error OUT NUMBER
, emp_cur OUT c_cursor
);
/* return children of submitted employee - only immediate level down*/
PROCEDURE employeeorg_child (
p_employee_number IN VARCHAR2
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
, p_error OUT NUMBER
, emp_cur OUT c_cursor
);
/* return supervisor of submitted employee */
PROCEDURE employeeorg_supervisor (
p_employee_number IN VARCHAR2
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
, p_error OUT NUMBER
, emp_cur OUT c_cursor
);
/* return siblings of submitted employee */
PROCEDURE employeeorg_siblings (
p_employee_number IN VARCHAR2
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
, p_error OUT NUMBER
, emp_cur OUT c_cursor
);
/* use any select statement and return cursor */
PROCEDURE dynsql (
p_sql IN VARCHAR2
, p_error OUT NUMBER
, emp_cur OUT c_cursor
);
/* get person id for provided employee number and eff_date */
FUNCTION get_person_id (
p_employee_number IN VARCHAR2
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
)
RETURN NUMBER;
/* get primary assignment id for provided employee number and eff_date */
FUNCTION get_primary_asg_id_e (
p_employee_number IN VARCHAR2
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
)
RETURN NUMBER;
/* get primary assignment id for provided person_id and eff_date */
FUNCTION get_primary_asg_id_p (
p_person_id IN NUMBER
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
)
RETURN NUMBER;
/* get employee number for provided person_id and eff_date */
FUNCTION get_employee_number_p (
p_person_id IN NUMBER
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
)
RETURN VARCHAR2;
/* get employee number for provided assignment_id and eff_date */
FUNCTION get_employee_number_a (
p_assignment_id IN NUMBER
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
)
RETURN VARCHAR2;
/* close cursor */
PROCEDURE employeecareer_close (emp_cur IN OUT c_cursor);
/* close cursor */
PROCEDURE dynsql_close (emp_cur IN OUT c_cursor);
END emp_refcur_pkg;
PACKAGE BODY
/* Formatted on 2003/01/27 13:13 (Formatter Plus v4.7.0) */
CREATE OR REPLACE PACKAGE BODY emp_refcur_pkg
AS
PROCEDURE employeecareer (
p_employee_number IN VARCHAR2
, p_error OUT NUMBER
, emp_cur OUT c_cursor
)
IS
-- EMPLOYEE NUMBER
-- EMPLOYEE NAME
-- EFFECTIVE START DATE
-- EFFECTIVE END DATE
-- JOB TITLE
-- JOB CODE
-- COID
-- UDN
-- ORGANIZATION NAME
stmt VARCHAR2 (2000);
BEGIN
stmt :=
'SELECT * FROM apps.hum_emp_career_hist_v WHERE employee_number IN ('
|| p_employee_number
|| ')';
OPEN emp_cur FOR stmt;
p_error := 0;
EXCEPTION
WHEN OTHERS
THEN
hum_do.default_exception ( CHR (10)
|| 'ERROR IN APPS.EMP_REFCUR_PKG.EMPLOYEE_CAREER'
|| CHR (10)
|| 'Submitted SQL := '
|| stmt
);
p_error := 2;
END employeecareer;
PROCEDURE employeeorg_vchild (
p_employee_number IN VARCHAR2
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
, p_level IN NUMBER
, p_error OUT NUMBER
, emp_cur OUT c_cursor
)
IS
-- LEVEL
-- EMPLOYEE_NAME
-- EMPLOYEE_NUMBER
-- SUPERVISOR FULL_NAME
-- SUPERVISOR EMPLOYEE NUMBER
BEGIN
OPEN emp_cur FOR
SELECT LEVEL
, LPAD (' ', 2 * LEVEL - 2)
|| emp.employee_name employee_name
, emp.employee_number, emp.supervisor_full_name
, emp.supervisor_number
FROM (SELECT ppf.full_name employee_name, ppf.employee_number
, peosp.full_name supervisor_full_name
, peosp.employee_number supervisor_number
FROM apps.per_assignments_f paf
, apps.per_people_f ppf
, apps.per_people_f peosp
WHERE peosp.person_id(+) = paf.supervisor_id
AND p_eff_date BETWEEN ppf.effective_start_date(+) AND ppf.effective_end_date(+)
AND p_eff_date BETWEEN paf.effective_start_date(+) AND paf.effective_end_date(+)
AND p_eff_date BETWEEN peosp.effective_start_date(+) AND peosp.effective_end_date(+)
AND paf.person_id(+) = ppf.person_id
AND paf.assignment_type = 'E'
AND paf.primary_flag = 'Y') emp
WHERE LEVEL <= p_level
CONNECT BY PRIOR emp.employee_number = emp.supervisor_number
START WITH emp.employee_number = p_employee_number;
p_error := 0;
EXCEPTION
WHEN OTHERS
THEN
hum_do.default_exception ( CHR (10)
|| 'ERROR IN APPS.EMP_REFCUR_PKG.EMPLOYEEORG_VCHILD'
);
p_error := 2;
END employeeorg_vchild;
PROCEDURE employeeorg_child (
p_employee_number IN VARCHAR2
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
, p_error OUT NUMBER
, emp_cur OUT c_cursor
)
IS
-- EMPLOYEE_NAME
-- EMPLOYEE_NUMBER
BEGIN
OPEN emp_cur FOR
SELECT ppf.full_name full_name, ppf.employee_number
FROM per_all_assignments_f paf
, per_all_people_f ppf
, (SELECT person_id
FROM per_all_assignments_f
WHERE person_id =
emp_refcur_pkg.get_person_id (p_employee_number
, p_eff_date
)
AND p_eff_date BETWEEN effective_start_date
AND effective_end_date) p_child
WHERE p_eff_date BETWEEN paf.effective_start_date
AND paf.effective_end_date
AND p_eff_date BETWEEN ppf.effective_start_date
AND ppf.effective_end_date
AND paf.person_id = ppf.person_id
AND paf.primary_flag = 'Y'
AND paf.assignment_type = 'E'
AND paf.supervisor_id = p_child.person_id;
p_error := 0;
EXCEPTION
WHEN OTHERS
THEN
hum_do.default_exception ( CHR (10)
|| 'ERROR IN APPS.EMP_REFCUR_PKG.EMPLOYEEORG_CHILD'
);
p_error := 2;
END employeeorg_child;
PROCEDURE employeeorg_supervisor (
p_employee_number IN VARCHAR2
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
, p_error OUT NUMBER
, emp_cur OUT c_cursor
)
IS
-- EMPLOYEE_NAME
-- EMPLOYEE_NUMBER
BEGIN
OPEN emp_cur FOR
SELECT ppf.full_name full_name, ppf.employee_number
FROM per_all_assignments_f paf
, per_all_people_f ppf
, (SELECT supervisor_id
FROM per_all_assignments_f
WHERE person_id =
emp_refcur_pkg.get_person_id (p_employee_number
, p_eff_date
)
AND p_eff_date BETWEEN effective_start_date
AND effective_end_date) p_sup
WHERE ppf.person_id = paf.person_id
AND paf.primary_flag = 'Y'
AND paf.assignment_type = 'E'
AND p_eff_date BETWEEN paf.effective_start_date
AND paf.effective_end_date
AND p_eff_date BETWEEN ppf.effective_start_date
AND ppf.effective_end_date
AND paf.person_id = p_sup.supervisor_id;
p_error := 0;
EXCEPTION
WHEN OTHERS
THEN
hum_do.default_exception ( CHR (10)
|| 'ERROR IN APPS.EMP_REFCUR_PKG.EMPLOYEEORG_SUPERVISOR'
);
p_error := 2;
END employeeorg_supervisor;
PROCEDURE employeeorg_siblings (
p_employee_number IN VARCHAR2
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
, p_error OUT NUMBER
, emp_cur OUT c_cursor
)
IS
-- EMPLOYEE_NAME
-- EMPLOYEE_NUMBER
BEGIN
OPEN emp_cur FOR
SELECT ppf.full_name full_name, ppf.employee_number
FROM per_all_assignments_f paf
, per_all_people_f ppf
, (SELECT supervisor_id
FROM per_all_assignments_f
WHERE person_id =
emp_refcur_pkg.get_person_id (p_employee_number
, p_eff_date
)
AND p_eff_date BETWEEN effective_start_date
AND effective_end_date) p_sup
WHERE p_eff_date BETWEEN paf.effective_start_date
AND paf.effective_end_date
AND p_eff_date BETWEEN ppf.effective_start_date
AND ppf.effective_end_date
AND paf.person_id = ppf.person_id
AND paf.primary_flag = 'Y'
AND paf.assignment_type = 'E'
AND paf.supervisor_id = p_sup.supervisor_id;
p_error := 0;
EXCEPTION
WHEN OTHERS
THEN
hum_do.default_exception ( CHR (10)
|| 'ERROR IN APPS.EMP_REFCUR_PKG.EMPLOYEEORG_SIBLINGS'
);
p_error := 2;
END employeeorg_siblings;
PROCEDURE dynsql (
p_sql IN VARCHAR2
, p_error OUT NUMBER
, emp_cur OUT c_cursor
)
IS
stmt VARCHAR2 (32000);
BEGIN
stmt := p_sql;
OPEN emp_cur FOR stmt;
p_error := 0;
EXCEPTION
WHEN OTHERS
THEN
hum_do.default_exception ( CHR (10)
|| 'ERROR IN APPS.EMP_REFCUR_PKG.DYNSQL'
|| CHR (10)
|| 'Submitted SQL := '
|| stmt
);
p_error := 2;
END dynsql;
FUNCTION get_person_id (
p_employee_number IN VARCHAR2
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
)
RETURN NUMBER
IS
t_value per_all_people_f.person_id%TYPE := 0; -- IF t_value has no value then 0 is returned to signify no data found or error --
BEGIN
SELECT person_id
INTO t_value
FROM per_all_people_f
WHERE p_eff_date BETWEEN effective_start_date AND effective_end_date
AND employee_number = p_employee_number;
RETURN t_value;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END get_person_id;
FUNCTION get_primary_asg_id_e (
p_employee_number IN VARCHAR2
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
)
RETURN NUMBER
IS
t_value per_all_assignments_f.assignment_id%TYPE := 0; -- IF t_value has no value then 0 is returned to signify no data found or error --
p_person_id PLS_INTEGER;
BEGIN
p_person_id := get_person_id (p_employee_number, p_eff_date);
IF p_person_id = 0
THEN
RETURN 0;
ELSE
SELECT assignment_id
INTO t_value
FROM per_all_assignments_f
WHERE p_eff_date BETWEEN effective_start_date AND effective_end_date
AND primary_flag = 'Y'
AND assignment_type = 'E'
AND person_id = p_person_id;
RETURN t_value;
END IF;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END get_primary_asg_id_e;
FUNCTION get_primary_asg_id_p (
p_person_id IN NUMBER
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
)
RETURN NUMBER
IS
t_value per_all_assignments_f.assignment_id%TYPE := 0; -- IF t_value has no value then 0 is returned to signify no data found or error --
BEGIN
SELECT assignment_id
INTO t_value
FROM per_all_assignments_f
WHERE p_eff_date BETWEEN effective_start_date AND effective_end_date
AND primary_flag = 'Y'
AND assignment_type = 'E'
AND person_id = p_person_id;
RETURN t_value;
EXCEPTION
WHEN OTHERS
THEN
RETURN 0;
END get_primary_asg_id_p;
/* get employee number for provided person_id and eff_date */
FUNCTION get_employee_number_p (
p_person_id IN NUMBER
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
)
RETURN VARCHAR2
IS
t_value per_all_people_f.employee_number%TYPE := '0'; -- IF t_value has no value then 0 is returned to signify no data found or error --
BEGIN
SELECT employee_number
INTO t_value
FROM per_all_people_f
WHERE p_eff_date BETWEEN effective_start_date AND effective_end_date
AND person_id = p_person_id;
RETURN t_value;
EXCEPTION
WHEN OTHERS
THEN
RETURN '0';
END get_employee_number_p;
/* get employee number for provided assignment_id and eff_date */
FUNCTION get_employee_number_a (
p_assignment_id IN NUMBER
, p_eff_date IN DATE DEFAULT TRUNC (SYSDATE)
)
RETURN VARCHAR2
IS
t_value per_all_people_f.employee_number%TYPE := '0'; -- IF t_value has no value then 0 is returned to signify no data found or error --
BEGIN
SELECT emp_refcur_pkg.get_employee_number_p (person_id, p_eff_date)
INTO t_value
FROM per_all_assignments_f
WHERE p_eff_date BETWEEN effective_start_date AND effective_end_date
AND assignment_id = p_assignment_id;
RETURN t_value;
EXCEPTION
WHEN OTHERS
THEN
RETURN '0';
END get_employee_number_a;
PROCEDURE employeecareer_close (emp_cur IN OUT c_cursor)
IS
BEGIN
CLOSE emp_cur;
END employeecareer_close;
PROCEDURE dynsql_close (emp_cur IN OUT c_cursor)
IS
BEGIN
CLOSE emp_cur;
END dynsql_close;
END emp_refcur_pkg;- admin's blog
- Log in to post comments

Comments
When using Java and Stored
When using Java and Stored Procedures/Functions remember to close everything, that means Connection, ResultSet, and CallableStatement. Cursors don't close themselves implicitly, unless the INIT.ora variable CLOSE_CACHED_OPEN_CURSORS is set to TRUE.
Thanks for the example.
hum_do package
Love this article. Thanks.
By the way where can we find the package hum_do source codes?
If you don't mind, can you please post it out?
Thanks a lot.