You are on page 1of 9

Cursors

Oracle uses work areas to execute SQL statements and store processing information.
A PL/SQL construct called a cursor lets you name a work area and access its stored
information.
There are two kinds of cursors: implicit and explicit.

PL/SQL implicitly declares a cursor for all SQL data manipulation statements,
including queries that return only one row.
For queries that return more than one row, you can explicitly declare a cursor to
process the rows individually. An example follows:

DECLARE
CURSOR c1 IS
SELECT empno, ename, job FROM emp WHERE deptno = 20;

The set of rows returned by a multi-row query is called the result set. Its size is
the number of rows that meet your search criteria.
Multi-row query processing is somewhat like file processing. For example, a COBOL
program opens a file, processes records, then closes the file.
Likewise, a PL/SQL program opens a cursor, processes rows returned by a query, then
closes the cursor.
Just as a file pointer marks the current position in an open file, a cursor marks
the current position in a result set.

You use the OPEN, FETCH, and CLOSE statements to control a cursor. The OPEN
statement executes the query associated with the cursor,
identifies the result set, and positions the cursor before the first row.
The FETCH statement retrieves the current row and advances the cursor to the next
row.
When the last row has been processed, the CLOSE statement disables the cursor.

Cursor FOR Loops


In most situations that require an explicit cursor, you can simplify coding by
using a cursor FOR loop instead of the OPEN, FETCH, and CLOSE statements.
A cursor FOR loop implicitly declares its loop index as a record that represents a
row fetched from the database.
Next, it opens a cursor, repeatedly fetches rows of values from the result set into
fields in the record, then closes the cursor
when all rows have been processed. In the following example, the cursor FOR loop
implicitly declares emp_rec as a record:

DECLARE
CURSOR c1 IS
SELECT ename, sal, hiredate, deptno FROM emp;
...
BEGIN
FOR emp_rec IN c1 LOOP
...
salary_total := salary_total + emp_rec.sal;
END LOOP;

To reference individual fields in the record, you use dot notation, in which a dot
(.) serves as the component selector.

RECORDS
You can use the %ROWTYPE attribute to declare a record that represents a row in a
table or a row fetched from a cursor. But, with a user-defined record, you can
declare fields of your own.

Records contain uniquely named fields, which can have different datatypes. Suppose
you have various data about an employee such as name, salary, and hire date. These
items are dissimilar in type but logically related. A record containing a field for
each item lets you treat the data as a logical unit.

Consider the following example:

DECLARE
TYPE TimeRec IS RECORD (hours SMALLINT, minutes SMALLINT);
TYPE MeetingTyp IS RECORD (
date_held DATE,
duration TimeRec, -- nested record
location VARCHAR2(20),
purpose VARCHAR2(50));

Notice that you can nest records. That is, a record can be a component of another
record.

PACKAGES

PL/SQL lets you bundle logically related types, variables, cursors, and subprograms
into a package.
Each package is easy to understand and the interfaces between packages are simple,
clear, and well defined. This aids application development.

Packages usually have two parts: a specification and a body.


The specification is the interface to your applications; it declares the types,
constants, variables, exceptions, cursors, and subprograms available for use. The
body defines cursors and subprograms and so implements the specification.

In the following example, you package two employment procedures:

CREATE PACKAGE emp_actions AS -- package specification


PROCEDURE hire_employee (empno NUMBER, ename CHAR, ...);
PROCEDURE fire_employee (emp_id NUMBER);
END emp_actions;

CREATE PACKAGE BODY emp_actions AS -- package body


PROCEDURE hire_employee (empno NUMBER, ename CHAR, ...) IS
BEGIN
INSERT INTO emp VALUES (empno, ename, ...);
END hire_employee;
PROCEDURE fire_employee (emp_id NUMBER) IS
BEGIN
DELETE FROM emp WHERE empno = emp_id;
END fire_employee;
END emp_actions;

Only the declarations in the package specification are visible and accessible to
applications.
Implementation details in the package body are hidden and inaccessible.

Packages can be compiled and stored in an Oracle database, where their contents can
be shared by many applications.
When you call a packaged subprogram for the first time, the whole package is loaded
into memory.
So, subsequent calls to related subprograms in the package require no disk I/O.
Thus, packages can enhance productivity and improve performance.

COLLECTION

a collection is an ordered group of elements, all of the same type. It is a general


concept that encompasses lists,
arrays, and other familiar datatypes. Each element has a unique subscript that
determines its position in the collection.

PL/SQL offers these collection types:

Index-by tables, also known as associative arrays, let you look up elements using
arbitrary numbers and strings for subscript values.
(They are similar to hash tables in other programming languages.)
Nested tables hold an arbitrary number of elements. They use sequential numbers as
subscripts. You can define equivalent SQL types, allowing nested tables to be
stored in database tables and manipulated through SQL.
Varrays (short for variable-size arrays) hold a fixed number of elements (although
you can change the number of elements at runtime). They use sequential numbers as
subscripts. You can define equivalent SQL types, allowing varrays to be stored in
database tables. They can be stored and retrieved through SQL, but with less
flexibility than nested tables.
Although collections can have only one dimension, you can model multi-dimensional
arrays by creating collections whose elements are also collections.

To use collections in an application, you define one or more PL/SQL types, then
define variables of those types. You can define collection types in a procedure,
function, or package. You can pass collection variables as parameters, to move data
between client-side applications and stored subprograms.

**************REF
CURSOR*****************************************************************************
*********************
***********************************************************************************
*****************************************

REF CURSOR types can be strong (restrictive) or weak (nonrestrictive). A strong REF
CURSOR type definition specifies a return type,
but a weak definition does not. Strong REF CURSOR types are less error prone
because the PL/SQL compiler lets you associate a strongly typed cursor variable
only with type-compatible queries. However, weak REF CURSOR types are more flexible
because the compiler lets you associate a weakly typed cursor variable with any
query.

Once you define a REF CURSOR type, you can declare cursor variables of that type.
You can use %TYPE to provide the datatype of a record variable.
Also, in the RETURN clause of a REF CURSOR type definition, you can use %ROWTYPE to
specify a record type that represents a row returned by a strongly
(not weakly) typed cursor variable

example

CREATE PACKAGE emp_data AS


TYPE EmpCurTyp IS REF CURSOR RETURN emp%ROWTYPE;
PROCEDURE open_emp_cv (emp_cv IN OUT EmpCurTyp,
choice IN NUMBER);
END emp_data;

CREATE PACKAGE BODY emp_data AS


PROCEDURE open_emp_cv (emp_cv IN OUT EmpCurTyp,
choice IN NUMBER) IS
BEGIN
IF choice = 1 THEN
OPEN emp_cv FOR SELECT * FROM emp WHERE comm IS NOT NULL;
ELSIF choice = 2 THEN
OPEN emp_cv FOR SELECT * FROM emp WHERE sal > 2500;
ELSIF choice = 3 THEN
OPEN emp_cv FOR SELECT * FROM emp WHERE deptno = 20;
END IF;
END open_emp_cv;
END emp_data;

Create or replace procedure test1(t_name in varchar2,col_name in varchar2)


is
type refcur is ref cursor;
r_cur refcur;

type tab_type is table of varchar2(100);


t_tab tab_type;

stmt varchar2(2000);

begin

stmt := 'Select '||col_name||'from'||t_name;

open r_cur for stmt;

fetch r_cur into t_tab;


close r_cur;

end ;

***********************************************************************************
*************************************

create or replace procedure test( p_deptno IN number


, p_cursor OUT SYS_REFCURSOR)
is
begin
open p_cursor FOR
select *
from emp
where deptno = p_deptno;
end test;

***********************************************************************************
*************************************
create or replace package REFCURSOR_PKG as
TYPE WEAK8i_REF_CURSOR IS REF CURSOR;
TYPE STRONG REF_CURSOR IS REF CURSOR RETURN EMP%ROWTYPE;
end REFCURSOR_PKG;

create or replace procedure test( p_deptno IN number


, p_cursor OUT REFCURSOR_PKG.STRONG
REF_CURSOR)
is
begin
open p_cursor FOR
select *
from emp
where deptno = p_deptno;
end test;

-------Calling ref-cursor from pl/sql

create or replace procedure test_call is


c_cursor REFCURSOR_PKG.STRONG REF_CURSOR;
r_emp c_emp%rowtype;
begin
test(10,c_cursor);
loop
fetch c_cursor into r_emp;
exit when c_cursor%notfound;
dbms_output.put_line(r_emp.name);
end loop;
close c_cursor;
Calling ref-cursor from pl/sql
end test_call

*********BULK COLLECT EXAMPLE

Bulk Collects enable a PL/SQL program to fetch many rows from a cursor in one call
instead of fetching one row at a time.
Bulk Binds also allow many similar DML statements to be executed with one call
instead of requiring a separate call for each.

DECLARE

TYPE NameList IS TABLE OF emp.ename%TYPE;

TYPE SalList IS TABLE OF emp.sal%TYPE;


CURSOR c1 IS SELECT ename, sal FROM emp_info;

names NameList;

sals SalList;

BEGIN

OPEN c1;

FETCH c1 BULK COLLECT INTO names, sals;

END;

*********BULK Bind EXAMPLE

DECLARE

TYPE NumList IS VARRAY(20) OF NUMBER;

depts NumList := NumList(10, 20); -- department numbers

BEGIN

FORALL i IN depts.FIRST..depts.LAST

UPDATE emp_info SET SAL=SAL*1.2 WHERE deptno = depts(i);

END;

DECLARE
TYPE ARRAY IS TABLE OF emp%ROWTYPE INDEX BY BINARY_INTEGER;
l_data array;
BEGIN
SELECT * BULK COLLECT INTO l_data FROM emp;
FORALL i IN 1 .. l_data.count
INSERT INTO emp VALUES l_data(i);
END;

create or replace procedure insert_million_limit


as
type array is table of big_all_tables%rowtype index by binary_integer;
l_data array;
cursor c1 is select OWNER, TABLE_NAME, COLUMN_NAME, DATA_TYPE,
DATA_TYPE_MOD, DATA_TYPE_OWNER, DATA_LENGTH,
DATA_PRECISION,
DATA_SCALE,
NULLABLE,
COLUMN_ID,

DATA_UPGRADED from big_all_tables;


begin
open c1;
loop
fetch c1 bulk collect into l_data LIMIT 5;
forall i in 1 .. l_data.count
insert into million_all_tables values l_data(i);
exit when c1%notfound;
end loop;
close c1;
end;
/

***********************************************************************************
***********************************************************************************
*********************************

***********UTL_FILE PACKAGE

UTL_FILE package that can read and write operating system files. The directory you
intend writing to has to be in your INIT.ORA file
(see UTL_FILE_DIR=� parameter). Before Oracle 7.3 the only means of writing a file
was to use DBMS_OUTPUT with the SQL*Plus SPOOL command.

Copy this example to get started:

DECLARE
fileHandler UTL_FILE.FILE_TYPE;
BEGIN
fileHandler := UTL_FILE.FOPEN('/tmp', 'myfile', 'w');
UTL_FILE.PUTF(fileHandler, 'Look ma, I''m writing to a file!!!\n');

UTL_FILE.FCLOSE(fileHandler);
EXCEPTION
WHEN utl_file.invalid_path THEN
raise_application_error(-20000, 'ERROR:
Invalid path for file or path not in INIT.ORA.');
END;
/

***********************************************************************************
***********************************************************************************
*********************************
********* EXCEPTION

|
|
-----------------------------------------------------
-----------------------------------------------------------
| |
|
| |
|
1.Predefined Exception (Implicitly raised) 2.Non-Predefined
Exception (Implicitly raised) 3.User-Defined Exception
1.Predefined Exception (Implicitly raised)----> One of approximately 20 errors that
occur in most often in PL-SQL code

ex

Begin
--
--
exception

when no_data_found then

when too_many_rows then

when zero_divide then

when others then

end;

2..Non-Predefined Exception (Implicitly raised)

declare

e_emps_remaining exception;

pragma exception_init(e_emps_remaining,-2292);

begin
delete from dept
where deptno = 10;
commit;

exception

when e_emps_remaining then


dbms_output.put_line('cannot delete from dept as child record exists');

end;

3.User-Defined Exception

declare

e_invalid_dept exception;

begin
update dept
set dept_name = 'new test'
where deptno = '55';

if sql%not found then


raise e_invalid_dept;
end if;

exception

when e_invalid_dept then


dbms_output.put_line('no such dept');
end;

*** RAISE_APPLICATION_ERROR

USED IN

---Executable Section
---Exception Section

declare

e_name exception;
pragma exception_init(ename,-22099)

begin

delete from emp


where ename = 'john';

if sql%not found then


raise_application_error(-22099,'this name is invalid');
end if;

exception

when ename then


---
end;

You might also like