Cursors Overview
A cursor is a pointer to a private SQL area that stores information about processing a specific SELECT
or DML statement.
Note:
The cursors that this topic explains are session cursors. A session cursor lives in session memory until the session ends, when it ceases to exist.
A cursor that is constructed and managed by PL/SQL is an implicit cursor. A cursor that you construct and manage is an explicit cursor.
You can get information about any session cursor from its attributes (which you can reference in procedural statements, but not in SQL statements).
To list the session cursors that each user session currently has opened and parsed, query the dynamic performance view V$OPEN_CURSOR
.
The number of cursors that a session can have open simultaneously is determined by:
-
The amount of memory available to the session
-
The value of the initialization parameter
OPEN_CURSORS
Note:
Generally, PL/SQL parses an explicit cursor only the first time the session opens it and parses a SQL statement (creating an implicit cursor) only the first time the statement runs.
All parsed SQL statements are cached. A SQL statement is reparsed only if it is aged out of the cache by a new SQL statement. Although you must close an explicit cursor before you can reopen it, PL/SQL need not reparse the associated query. If you close and immediately reopen an explicit cursor, PL/SQL does not reparse the associated query.
Topics
See Also:
-
Oracle Database Reference for information about the dynamic performance view
V$OPEN_CURSOR
-
Oracle Database Reference for information about the initialization parameter
OPEN_CURSORS
Implicit Cursors
An implicit cursor is a session cursor that is constructed and managed by PL/SQL. PL/SQL opens an implicit cursor every time you run a SELECT
or DML statement. You cannot control an implicit cursor, but you can get information from its attributes.
The syntax of an implicit cursor attribute value is SQL
attribute
(therefore, an implicit cursor is also called a SQL cursor). SQL
attribute
always refers to the most recently run SELECT
or DML statement. If no such statement has run, the value of SQL
attribute
is NULL
.
An implicit cursor closes after its associated statement runs; however, its attribute values remain available until another SELECT
or DML statement runs.
The most recently run SELECT
or DML statement might be in a different scope. To save an attribute value for later use, assign it to a local variable immediately. Otherwise, other operations, such as subprogram invocations, might change the value of the attribute before you can test it.
The implicit cursor attributes are:
-
SQL%BULK_ROWCOUNT
(see "Getting Number of Rows Affected by FORALL Statement" -
SQL%BULK_EXCEPTIONS
(see "Handling FORALL Exceptions After FORALL Statement Completes"
See Also:
"Implicit Cursor Attribute" for complete syntax and semantics
SQL%ISOPEN Attribute: Is the Cursor Open?
SQL%ISOPEN
always returns FALSE
, because an implicit cursor always closes after its associated statement runs.
SQL%FOUND Attribute: Were Any Rows Affected?
SQL%FOUND
returns:
-
NULL
if noSELECT
or DML statement has run -
TRUE
if aSELECT
statement returned one or more rows or a DML statement affected one or more rows -
FALSE
otherwise
Example 7-3 uses SQL%FOUND
to determine if a DELETE
statement affected any rows.
Example 7-3 SQL%FOUND Implicit Cursor Attribute
DROP TABLE dept_temp;
CREATE TABLE dept_temp AS
SELECT * FROM departments;
CREATE OR REPLACE PROCEDURE p (
dept_no NUMBER
) AUTHID CURRENT_USER AS
BEGIN
DELETE FROM dept_temp
WHERE department_id = dept_no;
IF SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE (
'Delete succeeded for department number ' || dept_no
);
ELSE
DBMS_OUTPUT.PUT_LINE ('No department number ' || dept_no);
END IF;
END;
/
BEGIN
p(270);
p(400);
END;
/
Result:
Delete succeeded for department number 270 No department number 400
SQL%NOTFOUND Attribute: Were No Rows Affected?
SQL%NOTFOUND
(the logical opposite of SQL%FOUND
) returns:
-
NULL
if noSELECT
or DML statement has run -
FALSE
if aSELECT
statement returned one or more rows or a DML statement affected one or more rows -
TRUE
otherwise
The SQL%NOTFOUND
attribute is not useful with the PL/SQL SELECT
INTO
statement, because:
-
If the
SELECT
INTO
statement returns no rows, PL/SQL raises the predefined exceptionNO_DATA_FOUND
immediately, before you can checkSQL%NOTFOUND
. -
A
SELECT
INTO
statement that invokes a SQL aggregate function always returns a value (possiblyNULL
). After such a statement, theSQL%NOTFOUND
attribute is alwaysFALSE
, so checking it is unnecessary.
SQL%ROWCOUNT Attribute: How Many Rows Were Affected?
SQL%ROWCOUNT
returns:
-
NULL
if noSELECT
or DML statement has run -
Otherwise, the number of rows returned by a
SELECT
statement or affected by a DML statement (anINTEGER
)
Note:
If a server is Oracle Database 12c or later and its client is Oracle Database 11g release 2 or earlier (or the reverse), then the maximum number that SQL%ROWCOUNT
returns is 4,294,967,295.
Example 7-4 uses SQL%ROWCOUNT
to determine the number of rows that were deleted.
If a SELECT
INTO
statement without a BULK
COLLECT
clause returns multiple rows, PL/SQL raises the predefined exception TOO_MANY_ROWS
and SQL%ROWCOUNT
returns 1, not the actual number of rows that satisfy the query.
The value of SQL%ROWCOUNT
attribute is unrelated to the state of a transaction. Therefore:
-
When a transaction rolls back to a savepoint, the value of
SQL%ROWCOUNT
is not restored to the value it had before the savepoint. -
When an autonomous transaction ends,
SQL%ROWCOUNT
is not restored to the original value in the parent transaction.
Example 7-4 SQL%ROWCOUNT Implicit Cursor Attribute
DROP TABLE employees_temp;
CREATE TABLE employees_temp AS
SELECT * FROM employees;
DECLARE
mgr_no NUMBER(6) := 122;
BEGIN
DELETE FROM employees_temp WHERE manager_id = mgr_no;
DBMS_OUTPUT.PUT_LINE
('Number of employees deleted: ' || TO_CHAR(SQL%ROWCOUNT));
END;
/
Result:
Number of employees deleted: 8
Explicit Cursors
An explicit cursor is a session cursor that you construct and manage. You must declare and define an explicit cursor, giving it a name and associating it with a query (typically, the query returns multiple rows). Then you can process the query result set in either of these ways:
-
Open the explicit cursor (with the
OPEN
statement), fetch rows from the result set (with theFETCH
statement), and close the explicit cursor (with theCLOSE
statement). -
Use the explicit cursor in a cursor
FOR
LOOP
statement (see "Processing Query Result Sets With Cursor FOR LOOP Statements".
You cannot assign a value to an explicit cursor, use it in an expression, or use it as a formal subprogram parameter or host variable. You can do those things with a cursor variable (see "Cursor Variables").
Unlike an implicit cursor, you can reference an explicit cursor or cursor variable by its name. Therefore, an explicit cursor or cursor variable is called a named cursor.
Topics
Declaring and Defining Explicit Cursors
You can either declare an explicit cursor first and then define it later in the same block, subprogram, or package, or declare and define it at the same time.
An explicit cursor declaration, which only declares a cursor, has this syntax:
CURSOR cursor_name [ parameter_list ] RETURN return_type;
An explicit cursor definition has this syntax:
CURSOR cursor_name [ parameter_list ] [ RETURN return_type ] IS select_statement;
If you declared the cursor earlier, then the explicit cursor definition defines it; otherwise, it both declares and defines it.
Example 7-5 declares and defines three explicit cursors.
See Also:
-
"Explicit Cursor Declaration and Definition" for the complete syntax and semantics of explicit cursor declaration and definition
Example 7-5 Explicit Cursor Declaration and Definition
DECLARE CURSOR c1 RETURN departments%ROWTYPE; -- Declare c1 CURSOR c2 IS -- Declare and define c2 SELECT employee_id, job_id, salary FROM employees WHERE salary > 2000; CURSOR c1 RETURN departments%ROWTYPE IS -- Define c1, SELECT * FROM departments -- repeating return type WHERE department_id = 110; CURSOR c3 RETURN locations%ROWTYPE; -- Declare c3 CURSOR c3 IS -- Define c3, SELECT * FROM locations -- omitting return type WHERE country_id = 'JP'; BEGIN NULL; END; /
Opening and Closing Explicit Cursors
After declaring and defining an explicit cursor, you can open it with the OPEN
statement, which does the following:
-
Allocates database resources to process the query
-
Processes the query; that is:
-
Identifies the result set
If the query references variables or cursor parameters, their values affect the result set. For details, see "Variables in Explicit Cursor Queries" and "Explicit Cursors that Accept Parameters".
-
If the query has a
FOR
UPDATE
clause, locks the rows of the result setFor details, see "SELECT FOR UPDATE and FOR UPDATE Cursors".
-
-
Positions the cursor before the first row of the result set
You close an open explicit cursor with the CLOSE
statement, thereby allowing its resources to be reused. After closing a cursor, you cannot fetch records from its result set or reference its attributes. If you try, PL/SQL raises the predefined exception INVALID_CURSOR
.
You can reopen a closed cursor. You must close an explicit cursor before you try to reopen it. Otherwise, PL/SQL raises the predefined exception CURSOR_ALREADY_OPEN
.
See Also:
-
"OPEN Statement" for its syntax and semantics
-
"CLOSE Statement" for its syntax and semantics
Fetching Data with Explicit Cursors
After opening an explicit cursor, you can fetch the rows of the query result set with the FETCH
statement. The basic syntax of a FETCH
statement that returns one row is:
FETCH cursor_name INTO into_clause
The into_clause
is either a list of variables or a single record variable. For each column that the query returns, the variable list or record must have a corresponding type-compatible variable or field. The %TYPE
and %ROWTYPE
attributes are useful for declaring variables and records for use in FETCH
statements.
The FETCH
statement retrieves the current row of the result set, stores the column values of that row into the variables or record, and advances the cursor to the next row.
Typically, you use the FETCH
statement inside a LOOP
statement, which you exit when the FETCH
statement runs out of rows. To detect this exit condition, use the cursor attribute %NOTFOUND
(described in "%NOTFOUND Attribute: Has No Row Been Fetched?"). PL/SQL does not raise an exception when a FETCH
statement returns no rows.
Example 7-6 fetches the result sets of two explicit cursors one row at a time, using FETCH
and %NOTFOUND
inside LOOP
statements. The first FETCH
statement retrieves column values into variables. The second FETCH
statement retrieves column values into a record. The variables and record are declared with %TYPE
and %ROWTYPE
, respectively.
Example 7-7 fetches the first five rows of a result set into five records, using five FETCH
statements, each of which fetches into a different record variable. The record variables are declared with %ROWTYPE
.
See Also:
-
"FETCH Statement" for its complete syntax and semantics
-
"FETCH Statement with BULK COLLECT Clause" for information about
FETCH
statements that return more than one row at a time
Example 7-6 FETCH Statements Inside LOOP Statements
DECLARE CURSOR c1 IS SELECT last_name, job_id FROM employees WHERE REGEXP_LIKE (job_id, 'S[HT]_CLERK') ORDER BY last_name; v_lastname employees.last_name%TYPE; -- variable for last_name v_jobid employees.job_id%TYPE; -- variable for job_id CURSOR c2 IS SELECT * FROM employees WHERE REGEXP_LIKE (job_id, '[ACADFIMKSA]_M[ANGR]') ORDER BY job_id; v_employees employees%ROWTYPE; -- record variable for row of table BEGIN OPEN c1; LOOP -- Fetches 2 columns into variables FETCH c1 INTO v_lastname, v_jobid; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE( RPAD(v_lastname, 25, ' ') || v_jobid ); END LOOP; CLOSE c1; DBMS_OUTPUT.PUT_LINE( '-------------------------------------' ); OPEN c2; LOOP -- Fetches entire row into the v_employees record FETCH c2 INTO v_employees; EXIT WHEN c2%NOTFOUND; DBMS_OUTPUT.PUT_LINE( RPAD(v_employees.last_name, 25, ' ') || v_employees.job_id ); END LOOP; CLOSE c2; END; /
Result:
Atkinson ST_CLERK Bell SH_CLERK Bissot ST_CLERK ... Walsh SH_CLERK ------------------------------------- Higgins AC_MGR Gruenberg FI_MGR Martinez MK_MAN ... Errazuriz SA_MAN
Example 7-7 Fetching Same Explicit Cursor into Different Variables
DECLARE CURSOR c IS SELECT e.job_id, j.job_title FROM employees e, jobs j WHERE e.job_id = j.job_id AND e.manager_id = 100 ORDER BY last_name; -- Record variables for rows of cursor result set: job1 c%ROWTYPE; job2 c%ROWTYPE; job3 c%ROWTYPE; job4 c%ROWTYPE; job5 c%ROWTYPE; BEGIN OPEN c; FETCH c INTO job1; -- fetches first row FETCH c INTO job2; -- fetches second row FETCH c INTO job3; -- fetches third row FETCH c INTO job4; -- fetches fourth row FETCH c INTO job5; -- fetches fifth row CLOSE c; DBMS_OUTPUT.PUT_LINE(job1.job_title || ' (' || job1.job_id || ')'); DBMS_OUTPUT.PUT_LINE(job2.job_title || ' (' || job2.job_id || ')'); DBMS_OUTPUT.PUT_LINE(job3.job_title || ' (' || job3.job_id || ')'); DBMS_OUTPUT.PUT_LINE(job4.job_title || ' (' || job4.job_id || ')'); DBMS_OUTPUT.PUT_LINE(job5.job_title || ' (' || job5.job_id || ')'); END; /
Result:
Sales Manager (SA_MAN)
Sales Manager (SA_MAN)
Stock Manager (ST_MAN)
Administration Vice President (AD_VP)
Stock Manager (ST_MAN)
PL/SQL procedure successfully completed.
Variables in Explicit Cursor Queries
An explicit cursor query can reference any variable in its scope. When you open an explicit cursor, PL/SQL evaluates any variables in the query and uses those values when identifying the result set. Changing the values of the variables later does not change the result set.
In Example 7-8, the explicit cursor query references the variable factor
. When the cursor opens, factor
has the value 2. Therefore, sal_multiple
is always 2 times sal
, despite that factor
is incremented after every fetch.
To change the result set, you must close the cursor, change the value of the variable, and then open the cursor again, as in Example 7-9.
Example 7-8 Variable in Explicit Cursor Query—No Result Set Change
DECLARE sal employees.salary%TYPE; sal_multiple employees.salary%TYPE; factor INTEGER := 2; CURSOR c1 IS SELECT salary, salary*factor FROM employees WHERE job_id LIKE 'AD_%'; BEGIN OPEN c1; -- PL/SQL evaluates factor LOOP FETCH c1 INTO sal, sal_multiple; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE('factor = ' || factor); DBMS_OUTPUT.PUT_LINE('sal = ' || sal); DBMS_OUTPUT.PUT_LINE('sal_multiple = ' || sal_multiple); factor := factor + 1; -- Does not affect sal_multiple END LOOP; CLOSE c1; END; /
Result:
factor = 2 sal = 4400 sal_multiple = 8800 factor = 3 sal = 24000 sal_multiple = 48000 factor = 4 sal = 17000 sal_multiple = 34000 factor = 5 sal = 17000 sal_multiple = 34000
Example 7-9 Variable in Explicit Cursor Query—Result Set Change
DECLARE sal employees.salary%TYPE; sal_multiple employees.salary%TYPE; factor INTEGER := 2; CURSOR c1 IS SELECT salary, salary*factor FROM employees WHERE job_id LIKE 'AD_%'; BEGIN DBMS_OUTPUT.PUT_LINE('factor = ' || factor); OPEN c1; -- PL/SQL evaluates factor LOOP FETCH c1 INTO sal, sal_multiple; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE('sal = ' || sal); DBMS_OUTPUT.PUT_LINE('sal_multiple = ' || sal_multiple); END LOOP; CLOSE c1; factor := factor + 1; DBMS_OUTPUT.PUT_LINE('factor = ' || factor); OPEN c1; -- PL/SQL evaluates factor LOOP FETCH c1 INTO sal, sal_multiple; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE('sal = ' || sal); DBMS_OUTPUT.PUT_LINE('sal_multiple = ' || sal_multiple); END LOOP; CLOSE c1; END; /
Result:
factor = 2 sal = 4400 sal_multiple = 8800 sal = 24000 sal_multiple = 48000 sal = 17000 sal_multiple = 34000 sal = 17000 sal_multiple = 34000 factor = 3 sal = 4400 sal_multiple = 13200 sal = 24000 sal_multiple = 72000 sal = 17000 sal_multiple = 51000 sal = 17000 sal_multiple = 51000
When Explicit Cursor Queries Need Column Aliases
When an explicit cursor query includes a virtual column (an expression), that column must have an alias if either of the following is true:
-
You use the cursor to fetch into a record that was declared with
%ROWTYPE
. -
You want to reference the virtual column in your program.
In Example 7-10, the virtual column in the explicit cursor needs an alias for both of the preceding reasons.
See Also:
Example 7-10 Explicit Cursor with Virtual Column that Needs Alias
DECLARE CURSOR c1 IS SELECT employee_id, (salary * .05) raise FROM employees WHERE job_id LIKE '%_MAN' ORDER BY employee_id; emp_rec c1%ROWTYPE; BEGIN OPEN c1; LOOP FETCH c1 INTO emp_rec; EXIT WHEN c1%NOTFOUND; DBMS_OUTPUT.PUT_LINE ( 'Raise for employee #' || emp_rec.employee_id || ' is $' || emp_rec.raise ); END LOOP; CLOSE c1; END; /
Result:
Raise for employee #114 is $550 Raise for employee #120 is $400 Raise for employee #121 is $410 Raise for employee #122 is $395 Raise for employee #123 is $325 Raise for employee #124 is $368.445 Raise for employee #145 is $700 Raise for employee #146 is $675 Raise for employee #147 is $600 Raise for employee #148 is $550 Raise for employee #149 is $525 Raise for employee #201 is $650
Explicit Cursors that Accept Parameters
You can create an explicit cursor that has formal parameters, and then pass different actual parameters to the cursor each time you open it. In the cursor query, you can use a formal cursor parameter anywhere that you can use a constant. Outside the cursor query, you cannot reference formal cursor parameters.
Tip:
To avoid confusion, use different names for formal and actual cursor parameters.
Example 7-11 creates an explicit cursor whose two formal parameters represent a job and its maximum salary. When opened with a specified job and maximum salary, the cursor query selects the employees with that job who are overpaid (for each such employee, the query selects the first and last name and amount overpaid). Next, the example creates a procedure that prints the cursor query result set (for information about procedures, see PL/SQL Subprograms). Finally, the example opens the cursor with one set of actual parameters, prints the result set, closes the cursor, opens the cursor with different actual parameters, prints the result set, and closes the cursor.
Topics
See Also:
-
"Explicit Cursor Declaration and Definition" for more information about formal cursor parameters
-
"OPEN Statement" for more information about actual cursor parameters
Example 7-11 Explicit Cursor that Accepts Parameters
DECLARE CURSOR c (job VARCHAR2, max_sal NUMBER) IS SELECT last_name, first_name, (salary - max_sal) overpayment FROM employees WHERE job_id = job AND salary > max_sal ORDER BY salary; PROCEDURE print_overpaid IS last_name_ employees.last_name%TYPE; first_name_ employees.first_name%TYPE; overpayment_ employees.salary%TYPE; BEGIN LOOP FETCH c INTO last_name_, first_name_, overpayment_; EXIT WHEN c%NOTFOUND; DBMS_OUTPUT.PUT_LINE(last_name_ || ', ' || first_name_ || ' (by ' || overpayment_ || ')'); END LOOP; END print_overpaid; BEGIN DBMS_OUTPUT.PUT_LINE('----------------------'); DBMS_OUTPUT.PUT_LINE('Overpaid Stock Clerks:'); DBMS_OUTPUT.PUT_LINE('----------------------'); OPEN c('ST_CLERK', 5000); print_overpaid; CLOSE c; DBMS_OUTPUT.PUT_LINE('-------------------------------'); DBMS_OUTPUT.PUT_LINE('Overpaid Sales Representatives:'); DBMS_OUTPUT.PUT_LINE('-------------------------------'); OPEN c('SA_REP', 10000); print_overpaid; CLOSE c; END; /
Result:
---------------------- Overpaid Stock Clerks: ---------------------- ------------------------------- Overpaid Sales Representatives: ------------------------------- Vishney, Clara (by 500) Abel, Ellen (by 1000) Ozer, Lisa (by 1500) PL/SQL procedure successfully completed.
Formal Cursor Parameters with Default Values
When you create an explicit cursor with formal parameters, you can specify default values for them. When a formal parameter has a default value, its corresponding actual parameter is optional. If you open the cursor without specifying the actual parameter, then the formal parameter has its default value.
Example 7-12 creates an explicit cursor whose formal parameter represents a location ID. The default value of the parameter is the location ID of company headquarters.
Example 7-12 Cursor Parameters with Default Values
DECLARE CURSOR c (location NUMBER DEFAULT 1700) IS SELECT d.department_name, e.last_name manager, l.city FROM departments d, employees e, locations l WHERE l.location_id = location AND l.location_id = d.location_id AND d.department_id = e.department_id ORDER BY d.department_id; PROCEDURE print_depts IS dept_name departments.department_name%TYPE; mgr_name employees.last_name%TYPE; city_name locations.city%TYPE; BEGIN LOOP FETCH c INTO dept_name, mgr_name, city_name; EXIT WHEN c%NOTFOUND; DBMS_OUTPUT.PUT_LINE(dept_name || ' (Manager: ' || mgr_name || ')'); END LOOP; END print_depts; BEGIN DBMS_OUTPUT.PUT_LINE('DEPARTMENTS AT HEADQUARTERS:'); DBMS_OUTPUT.PUT_LINE('--------------------------------'); OPEN c; print_depts; DBMS_OUTPUT.PUT_LINE('--------------------------------'); CLOSE c; DBMS_OUTPUT.PUT_LINE('DEPARTMENTS IN CANADA:'); DBMS_OUTPUT.PUT_LINE('--------------------------------'); OPEN c(1800); -- Toronto print_depts; CLOSE c; OPEN c(1900); -- Whitehorse print_depts; CLOSE c; END; /
Result is similar to:
DEPARTMENTS AT HEADQUARTERS: -------------------------------- Administration (Manager: Whalen) Purchasing (Manager: Himuro) Purchasing (Manager: Tobias) Purchasing (Manager: Baida) Purchasing (Manager: Li) Purchasing (Manager: Colmenares) Purchasing (Manager: Khoo) Executive (Manager: Yang) Executive (Manager: Garcia) Executive (Manager: King) Finance (Manager: Urman) Finance (Manager: Sciarra) Finance (Manager: Chen) Finance (Manager: Faviet) Finance (Manager: Gruenberg) Finance (Manager: Popp) Accounting (Manager: Higgins) Accounting (Manager: Gietz) -------------------------------- DEPARTMENTS IN CANADA: -------------------------------- Marketing (Manager: Davis) Marketing (Manager: Martinez) PL/SQL procedure successfully completed.
Adding Formal Cursor Parameters with Default Values
If you add formal parameters to a cursor, and you specify default values for the added parameters, then you need not change existing references to the cursor. Compare Example 7-13 to Example 7-11.
Example 7-13 Adding Formal Parameter to Existing Cursor
DECLARE CURSOR c (job VARCHAR2, max_sal NUMBER, hired DATE DEFAULT TO_DATE('31-DEC-1999', 'DD-MON-YYYY')) IS SELECT last_name, first_name, (salary - max_sal) overpayment FROM employees WHERE job_id = job AND salary > max_sal AND hire_date > hired ORDER BY salary; PROCEDURE print_overpaid IS last_name_ employees.last_name%TYPE; first_name_ employees.first_name%TYPE; overpayment_ employees.salary%TYPE; BEGIN LOOP FETCH c INTO last_name_, first_name_, overpayment_; EXIT WHEN c%NOTFOUND; DBMS_OUTPUT.PUT_LINE(last_name_ || ', ' || first_name_ || ' (by ' || overpayment_ || ')'); END LOOP; END print_overpaid; BEGIN DBMS_OUTPUT.PUT_LINE('-------------------------------'); DBMS_OUTPUT.PUT_LINE('Overpaid Sales Representatives:'); DBMS_OUTPUT.PUT_LINE('-------------------------------'); OPEN c('SA_REP', 10000); -- existing reference print_overpaid; CLOSE c; DBMS_OUTPUT.PUT_LINE('------------------------------------------------'); DBMS_OUTPUT.PUT_LINE('Overpaid Sales Representatives Hired After 2014:'); DBMS_OUTPUT.PUT_LINE('------------------------------------------------'); OPEN c('SA_REP', 10000, TO_DATE('31-DEC-2014', 'DD-MON-YYYY')); -- new reference print_overpaid; CLOSE c; END; /
Result:
------------------------------- Overpaid Sales Representatives: ------------------------------- Vishney, Clara (by 500) Abel, Ellen (by 1000) Ozer, Lisa (by 1500) ------------------------------------------------ Overpaid Sales Representatives Hired After 2014: ------------------------------------------------ Vishney, Clara (by 500) Ozer, Lisa (by 1500) PL/SQL procedure successfully completed.
Explicit Cursor Attributes
The syntax for the value of an explicit cursor attribute is cursor_name
immediately followed by attribute
(for example, c1%ISOPEN
).
Note:
Explicit cursors and cursor variables (named cursors) have the same attributes. This topic applies to all named cursors except where noted.
The explicit cursor attributes are:
-
%ISOPEN Attribute: Is the Cursor Open?
-
%FOUND Attribute: Has a Row Been Fetched?
-
%NOTFOUND Attribute: Has No Row Been Fetched?
-
%ROWCOUNT Attribute: How Many Rows Were Fetched?
If an explicit cursor is not open, referencing any attribute except %ISOPEN
raises the predefined exception INVALID_CURSOR
.
See Also:
"Named Cursor Attribute" for complete syntax and semantics of named cursor (explicit cursor and cursor variable) attributes
%ISOPEN Attribute: Is the Cursor Open?
%ISOPEN
returns TRUE
if its explicit cursor is open; FALSE
otherwise.
%ISOPEN
is useful for:
-
Checking that an explicit cursor is not already open before you try to open it.
If you try to open an explicit cursor that is already open, PL/SQL raises the predefined exception
CURSOR_ALREADY_OPEN
. You must close an explicit cursor before you can reopen it.Note:
The preceding paragraph does not apply to cursor variables.
-
Checking that an explicit cursor is open before you try to close it.
Example 7-14 opens the explicit cursor c1
only if it is not open and closes it only if it is open.
Example 7-14 %ISOPEN Explicit Cursor Attribute
DECLARE CURSOR c1 IS SELECT last_name, salary FROM employees WHERE ROWNUM < 11; the_name employees.last_name%TYPE; the_salary employees.salary%TYPE; BEGIN IF NOT c1%ISOPEN THEN OPEN c1; END IF; FETCH c1 INTO the_name, the_salary; IF c1%ISOPEN THEN CLOSE c1; END IF; END; /
%FOUND Attribute: Has a Row Been Fetched?
%FOUND
returns:
-
NULL
after the explicit cursor is opened but before the first fetch -
TRUE
if the most recent fetch from the explicit cursor returned a row -
FALSE
otherwise
%FOUND
is useful for determining whether there is a fetched row to process.
Example 7-15 loops through a result set, printing each fetched row and exiting when there are no more rows to fetch.
Example 7-15 %FOUND Explicit Cursor Attribute
DECLARE
CURSOR c1 IS
SELECT last_name, salary FROM employees
WHERE ROWNUM < 11
ORDER BY last_name;
my_ename employees.last_name%TYPE;
my_salary employees.salary%TYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO my_ename, my_salary;
IF c1%FOUND THEN -- fetch succeeded
DBMS_OUTPUT.PUT_LINE('Name = ' || my_ename || ', salary = ' || my_salary);
ELSE -- fetch failed
EXIT;
END IF;
END LOOP;
END;
/
Result:
Name = Faviet, salary = 9000 Name = Garcia, salary = 17000 Name = Gruenberg, salary = 12008 Name = Jackson, salary = 4800 Name = James, salary = 9000 Name = King, salary = 24000 Name = Miller, salary = 6000 Name = Nguyen, salary = 4200 Name = Williams, salary = 4800 Name = Yang, salary = 17000
%NOTFOUND Attribute: Has No Row Been Fetched?
%NOTFOUND
(the logical opposite of %FOUND
) returns:
-
NULL
after the explicit cursor is opened but before the first fetch -
FALSE
if the most recent fetch from the explicit cursor returned a row -
TRUE
otherwise
%NOTFOUND
is useful for exiting a loop when FETCH
fails to return a row, as in Example 7-16.
Example 7-16 %NOTFOUND Explicit Cursor Attribute
DECLARE
CURSOR c1 IS
SELECT last_name, salary FROM employees
WHERE ROWNUM < 11
ORDER BY last_name;
my_ename employees.last_name%TYPE;
my_salary employees.salary%TYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO my_ename, my_salary;
IF c1%NOTFOUND THEN -- fetch failed
EXIT;
ELSE -- fetch succeeded
DBMS_OUTPUT.PUT_LINE
('Name = ' || my_ename || ', salary = ' || my_salary);
END IF;
END LOOP;
END;
/
Result:
Name = Faviet, salary = 9000
Name = Garcia, salary = 17000
Name = Gruenberg, salary = 12008
Name = Jackson, salary = 4800
Name = James, salary = 9000
Name = King, salary = 24000
Name = Miller, salary = 6000
Name = Nguyen, salary = 4200
Name = Williams, salary = 4800
Name = Yang, salary = 17000
%ROWCOUNT Attribute: How Many Rows Were Fetched?
%ROWCOUNT
returns:
-
Zero after the explicit cursor is opened but before the first fetch
-
Otherwise, the number of rows fetched (an
INTEGER
)Note:
If a server is Oracle Database 12c or later and its client is Oracle Database 11g2 or earlier (or the reverse), then the maximum number that
SQL%ROWCOUNT
returns is 4,294,967,295.
Example 7-17 numbers and prints the rows that it fetches and prints a message after fetching the fifth row.
Example 7-17 %ROWCOUNT Explicit Cursor Attribute
DECLARE
CURSOR c1 IS
SELECT last_name FROM employees
WHERE ROWNUM < 11
ORDER BY last_name;
name employees.last_name%TYPE;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO name;
EXIT WHEN c1%NOTFOUND OR c1%NOTFOUND IS NULL;
DBMS_OUTPUT.PUT_LINE(c1%ROWCOUNT || '. ' || name);
IF c1%ROWCOUNT = 5 THEN
DBMS_OUTPUT.PUT_LINE('--- Fetched 5th row ---');
END IF;
END LOOP;
CLOSE c1;
END;
/
Result:
1. Abel
2. Ande
3. Atkinson
4. Baida
5. Banda
--- Fetched 5th row ---
6. Bates
7. Bell
8. Bernstein
9. Bissot
10. Bloom