REF Cursors
A REF CURSOR is a cursor variable — a pointer to a query result set that can be passed between PL/SQL subprograms and returned to clients (Java, Python, .NET). Unlike static cursors, a REF CURSOR's query is determined at runtime.
Strong vs Weak REF CURSOR
A strong REF CURSOR has a fixed return type; a weak one does not:
-- Strong: tied to employees%ROWTYPE — type-checked at compile time
TYPE t_emp_cursor IS REF CURSOR RETURN employees%ROWTYPE;
-- Weak: accepts any query — checked at runtime only
TYPE t_generic_cursor IS REF CURSOR;
-- SYS_REFCURSOR: Oracle's built-in weak REF CURSOR type (no declaration needed)
SYS_REFCURSOR is a predefined weak REF CURSOR type available in all Oracle versions from 9i onward. Use it instead of declaring your own weak type.
Basic REF CURSOR Usage
DECLARE
v_cursor SYS_REFCURSOR;
v_fname employees.first_name%TYPE;
v_lname employees.last_name%TYPE;
v_salary employees.salary%TYPE;
BEGIN
-- Open the cursor for a specific query
OPEN v_cursor FOR
SELECT first_name, last_name, salary
FROM employees
WHERE department_id = 90
ORDER BY salary DESC;
LOOP
FETCH v_cursor INTO v_fname, v_lname, v_salary;
EXIT WHEN v_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_fname || ' ' || v_lname || ': $' || v_salary);
END LOOP;
CLOSE v_cursor;
END;
/
Returning a REF CURSOR from a Procedure
The most common pattern: a procedure opens a cursor that the caller (or a client app) reads:
CREATE OR REPLACE PROCEDURE get_employees_by_dept(
p_dept_id IN employees.department_id%TYPE,
p_cursor OUT SYS_REFCURSOR
) IS
BEGIN
OPEN p_cursor FOR
SELECT e.employee_id,
e.first_name || ' ' || e.last_name AS full_name,
e.salary,
d.department_name
FROM employees e
JOIN departments d ON d.department_id = e.department_id
WHERE e.department_id = p_dept_id
ORDER BY e.salary DESC;
END get_employees_by_dept;
/
Calling from PL/SQL:
DECLARE
v_cur SYS_REFCURSOR;
v_emp_id employees.employee_id%TYPE;
v_name VARCHAR2(100);
v_salary employees.salary%TYPE;
v_dept departments.department_name%TYPE;
BEGIN
get_employees_by_dept(80, v_cur);
LOOP
FETCH v_cur INTO v_emp_id, v_name, v_salary, v_dept;
EXIT WHEN v_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_name || ' ($' || v_salary || ')');
END LOOP;
CLOSE v_cur;
END;
/
Returning a REF CURSOR from a Function
Functions can return SYS_REFCURSOR and be called from SQL:
CREATE OR REPLACE FUNCTION get_top_earners(p_n IN NUMBER DEFAULT 5)
RETURN SYS_REFCURSOR IS
v_cursor SYS_REFCURSOR;
BEGIN
OPEN v_cursor FOR
SELECT employee_id,
first_name || ' ' || last_name AS full_name,
salary
FROM employees
ORDER BY salary DESC
FETCH FIRST p_n ROWS ONLY;
RETURN v_cursor;
END get_top_earners;
/
-- Consume in PL/SQL
DECLARE
v_cur SYS_REFCURSOR := get_top_earners(3);
v_id NUMBER;
v_name VARCHAR2(100);
v_salary NUMBER;
BEGIN
LOOP
FETCH v_cur INTO v_id, v_name, v_salary;
EXIT WHEN v_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_name || ': $' || v_salary);
END LOOP;
CLOSE v_cur;
END;
/
Opening with Dynamic SQL
A REF CURSOR query can be built at runtime — combining the flexibility of dynamic SQL with the clean cursor interface:
CREATE OR REPLACE PROCEDURE search_employees(
p_column IN VARCHAR2,
p_value IN VARCHAR2,
p_cursor OUT SYS_REFCURSOR
) IS
v_sql VARCHAR2(500);
BEGIN
-- Build query dynamically — note bind variable :val to prevent injection
v_sql := 'SELECT employee_id, first_name, last_name, salary
FROM employees
WHERE ' || DBMS_ASSERT.SIMPLE_SQL_NAME(p_column) || ' = :val
ORDER BY employee_id';
OPEN p_cursor FOR v_sql USING p_value;
END search_employees;
/
-- Test
DECLARE
v_cur SYS_REFCURSOR;
v_id NUMBER;
v_fn VARCHAR2(50);
v_ln VARCHAR2(50);
v_sal NUMBER;
BEGIN
search_employees('JOB_ID', 'IT_PROG', v_cur);
LOOP
FETCH v_cur INTO v_id, v_fn, v_ln, v_sal;
EXIT WHEN v_cur%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_id || ': ' || v_fn || ' ' || v_ln);
END LOOP;
CLOSE v_cur;
END;
/
USING p_value). For column names, validate with DBMS_ASSERT.SIMPLE_SQL_NAME to block injection.
Passing REF CURSORs to Java / JDBC
In Java, a REF CURSOR OUT parameter maps to java.sql.ResultSet:
// Java example (not PL/SQL — shown for context)
CallableStatement cs = conn.prepareCall("{call get_employees_by_dept(?, ?)}");
cs.setInt(1, 80);
cs.registerOutParameter(2, OracleTypes.CURSOR);
cs.execute();
ResultSet rs = (ResultSet) cs.getObject(2);
while (rs.next()) {
System.out.println(rs.getString("full_name") + ": $" + rs.getDouble("salary"));
}
rs.close();
Strong REF CURSOR with %ROWTYPE
Use a strong cursor when the return type is always the same — Oracle validates column types at compile time:
DECLARE
TYPE t_emp_cur IS REF CURSOR RETURN employees%ROWTYPE;
v_cursor t_emp_cur;
v_emp employees%ROWTYPE;
BEGIN
OPEN v_cursor FOR
SELECT * FROM employees WHERE department_id = 60;
LOOP
FETCH v_cursor INTO v_emp;
EXIT WHEN v_cursor%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(v_emp.first_name || ': $' || v_emp.salary);
END LOOP;
CLOSE v_cursor;
END;
/
Strong vs Weak REF CURSOR — when to use which?
Strong REF CURSOR:
- The query return type is fixed and known at design time.
- You want compile-time type checking on the columns fetched.
- Defined using
TYPE t IS REF CURSOR RETURN some_type.
Weak REF CURSOR (SYS_REFCURSOR):
- The query varies at runtime (different tables, columns, or conditions).
- You are returning a cursor to a client that will determine the columns.
- You need one procedure signature to serve multiple query shapes.
Most production code uses SYS_REFCURSOR for OUT parameters returned to application layers, and strong REF CURSORs internally when the shape is fixed.
Summary
- A REF CURSOR is a cursor variable — a runtime pointer to a query result set.
- Strong REF CURSORs have a fixed return type (compile-time safety). Weak ones accept any query.
SYS_REFCURSORis Oracle's built-in weak REF CURSOR — no type declaration needed.- Pass REF CURSORs as
OUTparameters from procedures, orRETURNthem from functions. - Open with
OPEN v_cursor FOR sql_string USING bind_varsfor dynamic queries. - Always
CLOSEREF CURSORs when done — callers are responsible when the cursor is returned as an OUT parameter.