Using Cursor Attributes. Oracle Release 2 (9.2)
விளம்பரம்
விளம்பரம்
Using Cursor Attributes
■
■
■
■
■
■
If you pass a host cursor variable to PL/SQL, you cannot fetch from it on the server side unless you also open it there on the same server call.
You cannot use comparison operators to test cursor variables for equality, inequality, or nullity.
You cannot assign nulls to a cursor variable.
You cannot use
REF CURSOR
types to specify column types in a
CREATE TABLE or
CREATE VIEW
statement. So, database columns cannot store the values of cursor variables.
You cannot use a
REF CURSOR
type to specify the element type of a collection, which means that elements in a index-by table, nested table, or varray cannot store the values of cursor variables.
Cursors and cursor variables are not interoperable; that is, you cannot use one where the other is expected. For example, the following cursor
FOR
loop is not allowed because it attempts to fetch from a cursor variable:
DECLARE
TYPE EmpCurTyp IS REF CURSOR RETURN emp%ROWTYPE;
emp_cv EmpCurTyp;
...
BEGIN
...
FOR emp_rec IN emp_cv LOOP ... -- not allowed
END;
Using Cursor Attributes
Every explicit cursor and cursor variable has four attributes:
%FOUND
,
%ISOPEN
%NOTFOUND
, and
%ROWCOUNT
. When appended to the cursor or cursor variable, these attributes return useful information about the execution of a data manipulation statement. You can use cursor attributes in procedural statements but not in SQL statements.
Overview of Explicit Cursor Attributes
Explicit cursor attributes return information about the execution of a multi-row query. When an explicit cursor or a cursor variable is opened, the rows that satisfy the associated query are identified and form the result set. Rows are fetched from the result set.
6-34
PL/SQL User’s Guide and Reference
Using Cursor Attributes
%FOUND Attribute: Has a Row Been Fetched?
After a cursor or cursor variable is opened but before the first fetch,
%FOUND
yields
NULL
. Thereafter, it yields
TRUE
if the last fetch returned a row, or
FALSE
if the last fetch failed to return a row. In the following example, you use
%FOUND
to select an action:
LOOP
FETCH c1 INTO my_ename, my_sal, my_hiredate;
IF c1%FOUND THEN -- fetch succeeded
...
ELSE -- fetch failed, so exit loop
EXIT;
END IF;
END LOOP;
If a cursor or cursor variable is not open, referencing it with
%FOUND
raises the predefined exception
INVALID_CURSOR
.
%ISOPEN Attribute: Is the Cursor Open?
%ISOPEN
yields
TRUE
if its cursor or cursor variable is open; otherwise,
%ISOPEN yields
FALSE
. In the following example, you use
%ISOPEN
to select an action:
IF c1%ISOPEN THEN -- cursor is open
...
ELSE -- cursor is closed, so open it
OPEN c1;
END IF;
%NOTFOUND Attribute: Has a Fetch Failed?
%NOTFOUND
is the logical opposite of
%FOUND
.
%NOTFOUND
yields
FALSE
if the last fetch returned a row, or
TRUE
if the last fetch failed to return a row. In the following example, you use
%NOTFOUND
to exit a loop when
FETCH
fails to return a row:
LOOP
FETCH c1 INTO my_ename, my_sal, my_hiredate;
EXIT WHEN c1%NOTFOUND;
...
END LOOP;
Before the first fetch,
%NOTFOUND
evaluates to
NULL
. So, if
FETCH
never executes successfully, the loop is never exited. That is because the
EXIT WHEN
statement executes only if its
WHEN
condition is true. To be safe, you might want to use the following
EXIT
statement instead:
Interaction Between PL/SQL and Oracle
6-35
Using Cursor Attributes
EXIT WHEN c1%NOTFOUND OR c1%NOTFOUND IS NULL;
If a cursor or cursor variable is not open, referencing it with
%NOTFOUND
raises
INVALID_CURSOR
.
%ROWCOUNT Attribute: How Many Rows Fetched So Far?
When its cursor or cursor variable is opened,
%ROWCOUNT
is zeroed. Before the first fetch,
%ROWCOUNT
yields
0
. Thereafter, it yields the number of rows fetched so far.
The number is incremented if the last fetch returned a row. In the next example, you use
%ROWCOUNT
to take action if more than ten rows have been fetched:
LOOP
FETCH c1 INTO my_ename, my_deptno;
IF c1%ROWCOUNT > 10 THEN
...
END IF;
...
END LOOP;
If a cursor or cursor variable is not open, referencing it with
%ROWCOUNT
raises
INVALID_CURSOR
.
Table 6–1 shows what each cursor attribute yields before and after you execute an
OPEN
,
FETCH
, or
CLOSE
statement.
Table 6–1 Cursor Attribute Values
OPEN
First FETCH
Next FETCH(es)
%FOUND %ISOPEN %NOTFOUND %ROWCOUNT before
exception
FALSE exception exception
after
NULL
before
NULL
TRUE
TRUE
NULL
NULL
after before
TRUE
TRUE
TRUE
TRUE
FALSE
FALSE
1
1
0
0
Last FETCH
CLOSE after
TRUE
before
TRUE
after before after
FALSE
FALSE exception
TRUE
TRUE
TRUE
TRUE
FALSE
FALSE
FALSE
TRUE
TRUE exception data dependent data dependent data dependent data dependent exception
6-36
PL/SQL User’s Guide and Reference
Using Cursor Attributes
Table 6–1 Cursor Attribute Values
%FOUND %ISOPEN %NOTFOUND %ROWCOUNT
Notes:
1.
2.
Referencing
%FOUND
,
%NOTFOUND
, or
%ROWCOUNT
before a cursor is opened or after it is closed raises
INVALID_CURSOR
.
After the first
FETCH
, if the result set was empty,
%FOUND
yields
FALSE
,
%NOTFOUND yields
TRUE
, and
%ROWCOUNT
yields 0.
Some Examples of Cursor Attributes
Suppose you have a table named data_table
that holds data collected from laboratory experiments, and you want to analyze the data from experiment 1. In the following example, you compute the results and store them in a database table named temp
:
-- available online in file ’examp5’
DECLARE
num1 data_table.n1%TYPE; -- Declare variables
num2 data_table.n2%TYPE; -- having same types as
num3 data_table.n3%TYPE; -- database columns
result temp.col1%TYPE;
CURSOR c1 IS
SELECT n1, n2, n3 FROM data_table WHERE exper_num = 1;
BEGIN
OPEN c1;
LOOP
FETCH c1 INTO num1, num2, num3;
EXIT WHEN c1%NOTFOUND; -- TRUE when FETCH finds no more rows
result := num2/(num1 + num3);
INSERT INTO temp VALUES (result, NULL, NULL);
END LOOP;
CLOSE c1;
COMMIT;
END;
In the next example, you check all storage bins that contain part number
5469
, withdrawing their contents until you accumulate
1000
units:
-- available online in file ’examp6’
DECLARE
CURSOR bin_cur(part_number NUMBER) IS
SELECT amt_in_bin FROM bins
WHERE part_num = part_number AND amt_in_bin > 0
Interaction Between PL/SQL and Oracle
6-37
Using Cursor Attributes
ORDER BY bin_num
FOR UPDATE OF amt_in_bin;
bin_amt bins.amt_in_bin%TYPE;
total_so_far NUMBER(5) := 0;
amount_needed CONSTANT NUMBER(5) := 1000;
bins_looked_at NUMBER(3) := 0;
BEGIN
OPEN bin_cur(5469);
WHILE total_so_far < amount_needed LOOP
FETCH bin_cur INTO bin_amt;
EXIT WHEN bin_cur%NOTFOUND;
-- if we exit, there’s not enough to fill the order
bins_looked_at := bins_looked_at + 1;
IF total_so_far + bin_amt < amount_needed THEN
UPDATE bins SET amt_in_bin = 0
WHERE CURRENT OF bin_cur;
-- take everything in the bin
total_so_far := total_so_far + bin_amt;
ELSE -- we finally have enough
UPDATE bins SET amt_in_bin = amt_in_bin
- (amount_needed - total_so_far)
WHERE CURRENT OF bin_cur;
total_so_far := amount_needed;
END IF;
END LOOP;
CLOSE bin_cur;
INSERT INTO temp
VALUES (NULL, bins_looked_at, ’<- bins looked at’);
COMMIT;
END;
Overview of Implicit Cursor Attributes
Implicit cursor attributes return information about the execution of an
INSERT
,
UPDATE
,
DELETE
, or
SELECT INTO
statement. The values of the cursor attributes always refer to the most recently executed SQL statement. Before Oracle opens the
SQL
cursor, the implicit cursor attributes yield
NULL
.
Note: The
SQL
cursor has another attribute,
%BULK_ROWCOUNT
, designed for use with the
FORALL
statement. For more information, see
FORALL Iterations with the %BULK_ROWCOUNT Attribute" on page 5-43.
6-38
PL/SQL User’s Guide and Reference
Using Cursor Attributes
%FOUND Attribute: Has a DML Statement Changed Rows?
Until a SQL data manipulation statement is executed,
%FOUND
yields
NULL
.
Thereafter,
%FOUND
yields
TRUE
if an
INSERT
,
UPDATE
, or
DELETE
statement affected one or more rows, or a
SELECT INTO statement returned one or more rows.
Otherwise,
%FOUND
yields
FALSE
. In the following example, you use
%FOUND
to insert a row if a delete succeeds:
DELETE FROM emp WHERE empno = my_empno;
IF SQL%FOUND THEN -- delete succeeded
INSERT INTO new_emp VALUES (my_empno, my_ename, ...);
%ISOPEN Attribute: Always FALSE for Implicit Cursors
Oracle closes the
SQL
cursor automatically after executing its associated SQL statement. As a result,
%ISOPEN
always yields
FALSE
.
%NOTFOUND Attribute: Has a DML Statement Failed to Change Rows?
%NOTFOUND
is the logical opposite of
%FOUND
.
%NOTFOUND
yields
TRUE
if an
INSERT
,
UPDATE
, or
DELETE
statement affected no rows, or a
SELECT INTO statement returned no rows. Otherwise,
%NOTFOUND
yields
FALSE
.
%ROWCOUNT Attribute: How Many Rows Affected So Far?
%ROWCOUNT
yields the number of rows affected by an
INSERT
,
UPDATE
, or
DELETE statement, or returned by a
SELECT INTO
statement.
%ROWCOUNT
yields
0
if an
INSERT
,
UPDATE
, or
DELETE
statement affected no rows, or a
SELECT INTO statement returned no rows. In the following example, you use
%ROWCOUNT
to take action if more than ten rows have been deleted:
DELETE FROM emp WHERE ...
IF SQL%ROWCOUNT > 10 THEN -- more than 10 rows were deleted
...
END IF;
If a
SELECT INTO
statement returns more than one row, PL/SQL raises the predefined exception
TOO_MANY_ROWS
and
%ROWCOUNT
yields
1
, not the actual number of rows that satisfy the query.
Guidelines for Using Implicit Cursor Attributes
The values of the cursor attributes always refer to the most recently executed SQL statement, wherever that statement is. It might be in a different scope (for example, in a sub-block). So, if you want to save an attribute value for later use, assign it to a
Interaction Between PL/SQL and Oracle
6-39
Using Cursor Attributes
Boolean variable immediately. In the following example, relying on the
IF condition is dangerous because the procedure check_status might have changed the value of
%NOTFOUND
:
BEGIN
...
UPDATE parts SET quantity = quantity - 1 WHERE partno = part_id;
check_status(part_id); -- procedure call
IF SQL%NOTFOUND THEN -- dangerous!
...
END;
END;
You can improve the code as follows:
BEGIN
...
UPDATE parts SET quantity = quantity - 1 WHERE partno = part_id;
sql_notfound := SQL%NOTFOUND; -- assign value to Boolean variable
check_status(part_id);
IF sql_notfound THEN ...
END;
If a
SELECT INTO
statement fails to return a row, PL/SQL raises the predefined exception
NO_DATA_FOUND
whether you check
%NOTFOUND
on the next line or not.
Consider the following example:
BEGIN
...
SELECT sal INTO my_sal FROM emp WHERE empno = my_empno;
-- might raise NO_DATA_FOUND
IF SQL%NOTFOUND THEN -- condition tested only when false
... -- this action is never taken
END IF;
The check is useless because the
IF
condition is tested only when
%NOTFOUND
is false. When PL/SQL raises
NO_DATA_FOUND
, normal execution stops and control transfers to the exception-handling part of the block.
However, a
SELECT INTO
statement that calls a SQL aggregate function never raises
NO_DATA_FOUND
because aggregate functions always return a value or a null. In such cases,
%NOTFOUND
yields
FALSE
, as the following example shows:
BEGIN
...
SELECT MAX(sal) INTO my_sal FROM emp WHERE deptno = my_deptno;
-- never raises NO_DATA_FOUND
6-40
PL/SQL User’s Guide and Reference
பதிவிறக்கு
விளம்பரம்