A SQL cursor is a private Oracle SQL working area. There are two types of SQL cursor:
implicit or explicit cursor. The implicit cursor is used by Oracle server to test and parse the
SQL statements and the explicit cursors are declared by the programmers.
Using the implicit cursor, we can test the outcome of SQL statements in PL/SQL. For
example,
SQL%ROWCOUNT, return the number of rows affected;
SQL%FOUND, a BOOLEAN attribute indicating whether the recent SQL statement
matches to any row;
SQL%NOTFOUND, a BOOLEAN attribute indicating whether the recent SQL
statement does not match to any row;
SQL%ISOPEN, a BOOLEAN attribute and always evaluated as FALSE immediately
after the SQL statement is executed.
To write the explicit cursor, please refer to the following example. Note that a cursor
definition can array a number of arguments.
For example,
DECLARE
CURSORcsr_ac(p_nameVARCHAR2)IS
SELECTempno,name,sal
FROMemployee
WHEREnameLIKE'%p_name%';
BEGIN
FORrec_acINcsr_ac('LE')
LOOP
DBMS_OUTPUT.PUT_LINE(rec_ac.empno||''||rec_ac.name||'
'||v_sal);
ENDLOOP;
CLOSEcsr_ac;
END;
/
Another way of writing the above code, is to use the basic loop and the SQL%NOTFOUND
cursor, as shown in the following.
SQL>DECLARE
2CURSORcsr_ac(p_nameVARCHAR2)IS
1
3SELECTempno,ename,sal
4FROMemp
5
6WHEREenameLIKE'%SMITH%';
7
8v_aemp.empno%TYPE;
9v_bemp.ename%TYPE;
10v_cemp.sal%TYPE;
11
12BEGIN
13OPENcsr_ac('');
14LOOP
15FETCHcsr_acINTOv_a,v_b,v_c;
16EXITWHENcsr_ac%NOTFOUND;
17
18DBMS_OUTPUT.PUT_LINE(v_a||''||v_b||''||
v_c);
19
20ENDLOOP;
21CLOSEcsr_ac;
22END;
23
24
25/
OUTPUT
7369SMITH800
Cursors in PL/SQL
Every SQL statement executed by the RDBMS has a private SQL area that contains
information about the SQL statement and the set of data returned. In PL/SQL, a cursor is a
name assigned to a specific private SQL area for a specific SQL statement. There can be
either static cursors, whose SQL statement is determined at compile time, or dynamic cursors,
whose SQL statement is determined at runtime. Static cursors are covered in greater detail in
this section. Dynamic cursors in PL/SQL are implemented via the built-in package
DBMS_SQL. See the book Oracle Built-in Packages and the corresponding Oracle PL/SQL
Built-ins Pocket Reference, both from O'Reilly & Associates, for full coverage on
DBMS_SQL and the other built-in packages.
Explicit Cursors
Explicit cursors are SELECT statements that are DECLAREd explicitly in the declaration
section of the current block or in a package specification. Use OPEN, FETCH, and CLOSE in
the execution or exception sections of your programs.
1.1 Declaring explicit cursors
To use an explicit cursor, you must first declare it in the declaration section of a block or
package. There are three types of explicit cursor declarations:
A cursor header that contains a RETURN clause in place of the SELECT statement:
CURSOR company_cur (id_in IN NUMBER)
RETURN company%ROWTYPE IS
SELECT * FROM company;
where cursor_name is the name of the cursor as declared in the declaration section. The
arguments are required if the definition of the cursor contains a parameter list.
You must open an explicit cursor before you can fetch rows from that cursor. When the cursor
is opened, the processing includes the PARSE, BIND, OPEN, and EXECUTE statements.
This OPEN processing includes: determining an execution plan, associating host variables
and cursor parameters with the placeholders in the SQL statement, determining the result set,
and, finally, setting the current row pointer to the first row in the result set.
When using a cursor FOR loop, the OPEN is implicit in the FOR statement. If you try to open
a cursor that is already open, PL/SQL will raise an "ORA-06511: PL/SQL: cursor already
open" exception.
1.3 Fetching from explicit cursors
The FETCH statement places the contents of the current row into local variables. To retrieve
all rows in a result set, each row needs to be fetched. The syntax for a FETCH statement is:
FETCH cursor_name INTO record_or_variable_list;
Attribute
Description
TRUE if cursor is open.
%ISOPEN
FALSE if cursor is not open.
INVALID_CURSOR is raised if cursor has not been OPENed.
%FOUND
NULL before the first fetch.
TRUE if record was fetched successfully.
FALSE if no row was returned.
INVALID_CURSOR if cursor has been CLOSEd.
INVALID_CURSOR is raised if cursor has not been OPENed.
%NOTFOUND
NULL before the first fetch.
FALSE if record was fetched successfully.
TRUE if no row was returned.
INVALID_CURSOR if cursor has been CLOSEd.
INVALID_CURSOR is raised if cursor has not been OPENed.
%ROWCOUNT
The number of rows fetched from the cursor.
INVALID_CURSOR if cursor has been CLOSEd.
Frequently a cursor attribute is checked as part of a WHILE loop that fetches rows from a
cursor:
DECLARE
caller_rec caller_pkg.caller_cur%ROWTYPE;
BEGIN
OPEN caller_pkg.caller_cur;
LOOP
FETCH caller_pkg.caller_cur into caller_rec;
EXIT WHEN caller_pkg.caller_cur%NOTFOUND
OR
caller_pkg.caller_cur%ROWCOUNT > 10;
UPDATE call
SET caller_id = caller_rec.caller_id
WHERE call_timestamp < SYSDATE;
END LOOP;
CLOSE caller_pkg.caller_cur;
END;
2 Implicit Cursors
Whenever a SQL statement is directly in the execution or exception section of a PL/SQL
block, you are working with implicit cursors. These statements include INSERT, UPDATE,
DELETE, and SELECT INTO statements. Unlike explicit cursors, implicit cursors do not
need to be declared, OPENed, FETCHed, or CLOSEd.
SELECT statements handle the %FOUND and %NOTFOUND attributes differently from
explicit cursors. When an implicit SELECT statement does not return any rows, PL/SQL
immediately raises the NO_DATA_FOUND exception and control passes to the exception
section. When an implicit SELECT returns more than one row, PL/SQL immediately raises
the TOO_MANY_ROWS exception and control passes to the exception section.
Implicit cursor attributes are referenced via the SQL cursor. For example:
BEGIN
UPDATE activity SET last_accessed := SYSDATE
WHERE UID = user_id;
IF SQL%NOTFOUND THEN
INSERT INTO activity_log (uid,last_accessed)
VALUES (user_id,SYSDATE);
END IF
END;
SQL Attributes
Description
%ISOPEN
%FOUND
TRUE if one or more rows were inserted, updated, or deleted or if
only one row was selected.
FALSE if no row was selected, updated, inserted, or deleted.
NULL before the statement.
%NOTFOUND
TRUE if no row was selected, updated, inserted, or deleted.
FALSE if one or more rows were inserted, updated, or deleted.
%ROWCOUNT
%BULK_ROWCOUNT
(Oracle8i)
Use the RETURNING clause in INSERT, UPDATE, and DELETE statements to obtain data
modified by the associated DML statement. This clause allows you to avoid an additional
SELECT statement to query the results of the DML statement. For example:
BEGIN
UPDATE activity SET last_accessed := SYSDATE
WHERE UID = user_id
RETURNING last_accessed, cost_center
INTO timestamp, chargeback_acct;
By using WHERE CURRENT OF, you do not have to repeat the WHERE clause in the
SELECT statement. For example:
DECLARE
CURSOR wip_cur IS
SELECT acct_no, enter_date FROM wip
WHERE enter_date < SYSDATE -7
FOR UPDATE;
BEGIN
FOR wip_rec IN wip_cur
LOOP
INSERT INTO acct_log (acct_no, order_date)
VALUES (wip_rec.acct_no, wip_rec.enter_
date);
DELETE FROM wip
WHERE CURRENT OF wip_cur;
END LOOP;
END;
3 Cursor Variables
A cursor variable is a data structure that points to a cursor object, which in turn points to the
cursor's result set. You can use cursor variables to more easily retrieve rows in a result set
from client and server programs. You can also use cursor variables to hide minor variations in
queries.
The syntax for a REF_CURSOR type is:
TYPE ref_cursor_name IS REF CURSOR
[RETURN record_type];
If you do not include a RETURN clause, then you are declaring a weak REF CURSOR.
Cursor variables declared from weak REF CURSORs can be associated with any query at
runtime. A REF CURSOR declaration with a RETURN clause defines a "strong" REF
CURSOR. A cursor variable based on a strong REF CURSOR can be associated with queries
whose result sets match the number and datatype of the record structure after the RETURN at
runtime.
To use cursor variables, you must first create a REF_CURSOR type, then declare a cursor
variable based on that type.
The following example shows the use of both weak and strong REF CURSORs:
DECLARE
-- Create a cursor type based on the companies
table.
TYPE company_curtype IS REF CURSOR
RETURN companies%ROWTYPE;
-- Create the variable based on the REF CURSOR.
company_cur company_curtype;
-- And now the weak, general approach.
FETCH and CLOSE a cursor variable using the same syntax as for explicit cursors. There are
a number of restrictions on cursor variables:
Cursor variables cannot be declared in a package since they do not have a persistent
state.
You cannot use the FOR UPDATE clause with cursor variables.
You cannot assign NULLs to a cursor variable nor use comparison operators to test for
equality, inequality, or nullity.
You cannot use RPCs to pass cursor variables from one server to another.
Cursor variables cannot be used with the dynamic SQL built-in package DBMS_SQL
Overview
PL/SQL is the Oracle's extension to SQL with design features of programming languages. The
data manipulation and query statements are included in the procedural units of codes. PL/SQL
allows the applications to be written in a PL/SQL procedure or a package and stored at Oracle
server, where these PL/SQL codes can be used as shared libraries, or applications, thus
enhancing the integration and code reuse. Moreover, the Oracle server pre-compiles PL/SQL
codes prior to the actual code execution and thus improving the performance.
The basic PL/SQL code structure is :
DECLARE -- optional, which declares and define variables, cursors and user-defined
exceptions.
BEGIN -- mandatory
- SQL statements
- PL/SQL statements
EXCEPTION -- optional, which specifies what actions to take when error occurs.
END; -- mandatory
For example, the following PL/SQL code block declares an integer v1, assigns it with value 3
and print out the value.
DECLARE
v1NUMBER(3);
BEGIN
v1:=3;
DBMS_OUTPUT.PUT_LINE('v1='||v1);
END;
Note that DBMS_OUTPUT is an Oracle-supplied PL/SQL package and PUT_LINE is one of
the packaged procedures. It displays the values on the SQL*Plus terminal which must be
enabled with SET SERVEROUTPUT ON first. To execute this code sample, login into
SQL*Plus, and type
SQL>SETSERVEROUTPUTON
DECLARE
v1NUMBER(3);
BEGIN
v1:=3;
DBMS_OUTPUT.PUT_LINE('v1='||v1);
END;
/
Note that a PL/SQL block is terminated by a slash / or a line byitself.
Handling Variables
Variables must be declared first before the usage. The PL/SQL variables can be a
scalar type such as DATE, NUMBER, VARCHAR(2), DATE, BOOLEAN, LONG and
CHAR, or a composite type, such array type VARRAY.
Only TRUE and FALSE can be assigned to BOOLEAN type of variables.
AND, OR, NOT operators can be used to connect BOOLEAN values.
% TYPE attribute can be used to define a variable which is of type the same as a
database column's type definition.
Users can customize the variable types by using TYPE ... IS ... statement.
The following code block illustrates the use of TYPE..IS... and VARRAY. In this sample, a
type v_arr is defined as an variable array of maximum 25 elements which are of type
NUMBER(3). Then a variable v1 is defined as type v_arr . This sample code also
demonstrates the use of %TYPE attribute.
DECLARE
TYPEv_arrISVARRAY(25)ofNUMBER(3);
v1v_arr;
10
v_empnoemployee.empno%TYPE;
BEGIN
v1(2):=3;
DBMS_OUTPUT.PUT_LINE('TheValueofv1(2)='||v1(2));
v_empno:=4;
END;
Coding Guidelines
INSERTINTOemployeeVALUES(6,'TOMLEE',10000);
UPDATEemployeeSETsal=sal+5000WHEREempno=6;
SELECTsalINTOv_salFROMemployeeWHEREempno=6;
DBMS_OUTPUT.PUT_LINE('Salaryincreasedto'||v_sal);
DELETEFROMemployeeWHEREempno=6;
11
COMMIT;
END;
/
Control Structures
Conditions checking
IF <condition> THEN
[ELSIF <condition> THEN]
[ELSE <condition> THEN]
END IF;
Basic loops.
LOOP
...
EXIT WHEN <condition>
END LOOP;
FOR loop.
WHILE loop.
12
A function is called as part of an expression. For example, the function sal_ok might be
called as follows:
IF sal_ok(new_sal, new_title) THEN ...
13
The specification holds public declarations, which are visible to your application. The body
holds implementation details and private declarations, which are hidden from your
application. As shown in the following figure, you can think of the specification as an
operational interface and of the body as a "black box":
You can debug, enhance, or replace a package body without changing the interface (package
specification) to the package body.
For example, we want to create a simple package providing three functions: hire_employee,
fire_employee and raise_salary.
First we created the package specification.
CREATEORREPLACEPACKAGEtestASpackagespec
TYPElistISVARRAY(25)ofNUMBER(3);
PROCEDUREhire_employee(emp_idINTEGER,nameVARCHAR2);
PROCEDUREfire_employee(emp_idINTEGER);
PROCEDUREraise_salary(emp_idINTEGER,amountREAL);
ENDtest;
/
Then we created the package body.
CREATEORREPLACEPACKAGEBODYtestASpackagebody
PROCEDUREhire_employee(emp_idINTEGER,nameVARCHAR2)IS
BEGIN
INSERTINTOemployeeVALUES(emp_id,name,1000);
ENDhire_employee;
PROCEDUREfire_employee(emp_idINTEGER)IS
BEGIN
DELETEFROMemployeeWHEREempno=emp_id;
14
ENDfire_employee;
PROCEDUREraise_salary(emp_idINTEGER,amountREAL)IS
BEGIN
DBMS_OUTPUT.PUT_LINE('IncreaseSalary:'||
to_char(amou
nt));
UPDATEemployeeSETsal=sal+amountWHEREempno=
emp_id;
ENDraise_salary;
ENDtest;
/
To compile the package, we can either type them into SQL*Plus terminal. And Oracle server
will compile and store the package, or save them into separate files and compile them from
SQL*Plus. Assume the package spec is stored in a file named spec, and the body is stored in
another file named body. The following shows how to compile the package and make the
procedure call at SQL*Plus.
SQL> SET SERVEROUTPUT ON
SQL> VARIABLE num NUMBER
SQL> @spec
SQL> @body
SQL> exec test.raise_salary(1,1000);
15