Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1of 167

Index

Chapter page No

1 introduction to pl/sql 02

2 control statements 16

3 Loops 25

4 cursors 32

5 collection & records 52

6 Exception Handling 80

7 PL/SQL subprograms 97

8 stored procedures 105

9 stored functions 115

10 packages 128

11 triggers 144

12 DML error logging 164

13 UTL_FILE package 166

14 Dynamic SQL 169

15 Working with LOBs 175

16 Wrapping PL/SQL code 179

Introduction to PL/SQL

1
PL/SQL is oracle corporation’s procedural extensions to SQL . PL/SQL is a database-
oriented programming language that extends Oracle SQL with procedural capabilities
by adding programming constructs found in procedural languages, resulting in a
structural language that is more powerful than SQL.
PL/SQL was developed by Oracle in early 90’s to enhance the capabilities of SQL.
A procedural language allows writing a program that specifies a list of operations to be
performed sequentially to achieve the desired result.

PL/SQL is not a stand-alone programming language. PL/SQL is a part of ORACLE


RDBMS and it can reside in two environments the client and the server. As a result it is
easy to move PL/SQL programs between server-side and client-side applications.

Benefits of PL/SQL :-

Improved performance :-

PL/SQL has several advantages . For example using SQL*PLUS we can send SQL
statement to the ORACLE server one at a time. Each SQL statement results in another
call to the ORACLE server hence multiple SELECT statements will result in multiple
round trips adding significantly to the network traffic.

When these SQL statements are combined into a PL/SQL block they are sent to the

ORACLE server as a single unit. The SQL statements in PL/SQL block are executed at
the server. The server sends the result of these SQL statements back to the client also
as a single unit. So number of round trips are reduced and performance improves.

Portability:-

You can move PL/SQL programs to any host environment (operating system or
platform) that supports ORACLE and PL/SQL. In other words PL/SQL programs can run
anywhere the ORACLE server can run.

Control structures :-

PL/SQL Control Structures allows you to do :-


 Execute sequence of statements conditionally.
 Execute sequence of statements iteratively in a loop.

2
Error handling :-

The error handling functionality in PL/SQL allows you to do following

 Process ORACLE server errors with exception-handling routines.


 Declare user defined exceptions and process them with exception-handling
routines.

What PL/SQL supports :-

 PL/SQL supports to execute a block of statements as unit


 PL/SQL supports variables and constants
 PL/SQL supports conditional constructs (IF)
 PL/SQL supports iteration control statements (loops)
 PL/SQL supports error handling using exceptions
 PL/SQL supports to define composite data types
 PL/SQL supports to execute a block using db triggers
 PL/SQL supports to store and share the code using sub-programs
 PL/SQL supports Object Oriented Programming
 PL/SQL supports Web Applications and Pages

PL/SQL ENVIRONMENT :-

Blocks of PL/SQL are passed to and processed by a PL/SQL engine , which may reside
within the tool or within the oracle server. The engine that is used depends on where
the pl/sql block is being invoked from.

Pl/sql engine separates the sql statements and sends them individually to the SQL
statement executor.A single transfer is required to send the block from the application
to the oracle server.thus improves performance , especially in a client /server network.

PL/SQL Blocks:- PL/SQL Blocks are two types

 Anonymous
 Named

3
Anonymous Block:- Anonymous PL/SQL Block consists of three sections:

The Declaration section (Optional).


The Execution section (Mandatory).
The Exception (or Error) Handling section (Optional).

Declaration Section:-The PL/SQL Block Declare section should start with keyword
called ‘DECLARE‘.Declaration section is not mandatory in a block. Declaration section is
used to declare any placeholders (that stores data temporarily) like variables,
constants, records and cursors which are used in the execution section.

Execution Section:-

The PL/SQL Block Execution section should start with keyword ‘BEGIN’ and should end
with keyword ‘END’. This section is a mandatory and it is used to write the
programming logic to achieve business requirement. Constructs like loops, conditional
statement and SQL statements are part of execution section.

Exception Section:

The PL/SQL Block Exception section should start with keyword ‘EXCEPTION’.
Exception section is not mandatory. This section is used to handle any error/exception
raised in the program. Exception handling section is the place where exception
handlers are defined to handle the run time errors.

DBMS_OUTPUT.PUT_LINE :-

This statement is used to display information on the screen. It is very helpful when you
want to see how your PL/SQL block is executed and you might want to see how
variables change their values throughout the program in order to debug it.

Ex :- DBMS_OUTPUT.PUT_LINE(‘hello’);

DBMS_OUTPUT.PUT_LINE writes information to the buffer for storage once program


has been completed the information from the buffer is displayed on the screen. The
size of the buffer can be set between 2000 and 1000000 bytes. To display messages
the following statements must be executed before you start pl/sql block

4
Syntax:-

SQL>SET SERVEROUTPUT [ON |OFF] [SIZE}

SQL>SET SERVEROUTPUT ON

SQL> SET SERVEROUTPUT ON SIZE 5000 ;

A simple PL/SQL block example without DECLARATION AND EXCEPTION


section (as these are not mandatory) : 

BEGIN
DBMS_OUTPUT.PUT_LINE('Welcome to – Naresh IT');
END;
Comments:-

Comments can be used in PL/SQL code blocks in either of the following form:

-- Single line comment

/* */ Multi line comment

PL/SQL Variables :-

Variables can be used for :

 Temporary storage

 Manipulation of stored values.

 Reusability

 Ease of maintance

A data variable can be declared as:

<VariableName> [CONSTANT] DATATYPE [NOT NULL] [:=| DEFAULT IntialValue]

Where,

VariableName is the name of the variable.

CONSTANT flags that placeholder as a constant.

DATATYPE is a valid PL/SQL datatype

DEFAULT Initial Value is an optional specification that allows initializing a


variable.

PL/SQL Data Types:-Every variable ,constant and parameter has a data type that
determines its storage format and valid range of values, and operations that can be
performed on it. PL/SQL provides many predefined data types.

Categories of Predefined PL/SQL Data Types:-

Data Type Category Data Description

Scalar Single values with no internal components.

5
Composite Data items that have internal components that
can be accessed individually.

Reference Pointers to other data items.

Large Object (LOB) Pointers to large objects such as images or text etc

Scalar datatypes :-

Variable of scalar datatype holds a single value.

6
Number(n,m) Blob
Char(n) Clob
Varchar2(n) Nclob
Date Bfile
Timestamp PLS_integer
Interval Day to Second Binary_integer
Interval Year to Month Binary_float
Long Binary_Double
Long raw Boolean
Raw
Declaring Variables:-

In any programming language variables play a vital role in building program logic. We
cannot imagine a code without using variables. Variables are like placeholders we use
them to store/manipulate data at the time of PL/SQL block execution.

Syntax :-

variablename datatype(size);

Example :-

x NUMBER;
Declaring a constant

ProductPrice CONSTANT Numeric(8, 1) :=324765.49;

The following returns an error:

ProductPrice CONSTANT Numeric(8,1);

Declaring a variable with an initial value

AuthorName Varchar2(20) := ‘VIJAY KUMAR’;

Declaring a variable as NOTNULL

BookName Varchar2(20) NOTNULL := “Oracle For Professionals”;

Assigning Values To Variables:-

A variable can be assigned a value by Using the assignment operator :=

Syntax:-

7
<VariableName> := <Value>;

X := 10;

Composite datatypes :-

Composite datatype allows group of elements.

 PL/SQL table

 PL/SQL record

Reference Types :-

%TYPE :-

Declaring a variable according to :-

 A database column datatype


 Another previously declared variable.
Example:-

veno emp.empno%type;
In the above example datatype declared for empno in emp table is assigned to
Variable veno.
%ROWTYPE :-
The %ROWTYPE attribute provides a record type that represents a row in a database
table. The record can store an entire row of data selected from the table or fetched
from a cursor or cursor variable.

Example:-

r emp%rowtype;

in the above example variable r can store an entire row of data fetched from table
emp.

a sample PL/SQL program to add two numbers

SQL>ed prg1

DECLARE
a NUMBER(2);
b NUMBER(2);
c NUMBER(3);
BEGIN
a := 10;
b := 20;
c := a+b;
dbms_output.put_line(c);
END;
/

8
SQL>@prg1
Output :- 30
When the above program is rerun the program displays the same output 30 because
variables a,b assigned with static values . to input values for a , b after running the
Program use substitution variables prefixed with “&”.
Program using Substitution Variables :-
SQL>ed prg2

DECLARE
a NUMBER(2);
b NUMBER(2);
c NUMBER(3);
BEGIN
a := &a;
b := &b;
c := a+b;
dbms_output.put_line(c);
END;
/

SQL>@prg2
Enter value for a :- 40
Enter value for b := 70
Output :- 110
Program to find biggest and smallest number :-
DECLARE
a number(2);
b number(2);
BEGIN
a := &a;
b := &b;
DBMS_OUTPUT.PUT_LINE('big='||greatest(a,b));
DBMS_OUTPUT.PUT_LINE('small='||least(a,b));
END;
/
After executing the program
Enter value for a :-50
Enter value for b :-90
Output :- big=90
Small=50
Interacting With Oracle Server From PL/SQL :-

9
to Interact with oracle server from PL/SQL , SQL statements needs to embedded in
PL/SQL program.the following statements can be directly embedded in PL/SQL program
DML statements (INSERT,UPDATE,DELETE)
DRL statement (SELECT)
TCL statements (COMMIT,ROLLBACK)
Retrieving Data From Database Using PL/SQL :-

In PL/SQL in SELECT statement INTO clause is mandatory and occurs between SELECT
and FROM clause. It is used to specify the names of the variables to hold values
returned by SELECT statement.we must use one variable for one item selected.
Program to Input Empno and Print Name and Salary of the Employee :-
DECLARE
vempno Number(4);
vename Varchar2(20);
vsal Number(7,2);
BEGIN
/* Using assignment operator. */
vEmpno := &Empno;
/* Using direct assignment from table column to variables */
SELECT ename,sal INTO vename,vsal
FROM EMP
WHERE EMPNO=vEmpno;
/* Printing a name and salary. */
DBMS_OUTPUT.PUT_LINE(‘Name :- ‘||vename);
DBMS_OUTPUT.PUT_LINE(‘Sal :- ‘||vsal);
END;
SQL>@prg3
Enter Empno :- 7369
output :- Name :- SMITH
Sal :- 800
Program to Input Empno and Print Experience of the Employee :-
DECLARE
v_eno emp.empno%type;
v_doj emp.hiredate%type;
v_expr number(2);
BEGIN

10
v_eno := &eno;
SELECT hiredate into v_doj
FROM emp
WHERE empno = v_eno;
v_expr := round((sysdate-v_doj)/365);
DBMS_OUTPUT.PUT_LINE('Expr= '||v_expr||' years');
END;
/
Programt to Input Empno and Calculate Total Salary Paid to Employee Till Last
Month ?

DECLARE
v_eno emp.empno%type;
v_sal emp.sal%type;
v_comm emp.comm%type;
v_doj emp.hiredate%type;
v_totsal number(11,2);
BEGIN
v_eno := &eno;
SELECT sal,comm,hiredate into v_sal,v_comm,v_doj
FROM emp
WHERE empno=v_eno;
v_totsal := round(months_between(ADD_MONTHS(LAST_DAY(sysdate),-
1),v_doj)*(v_sal+nvl(v_comm,0)));
DBMS_OUTPUT.PUT_LINE('TOTAL SALARY='||v_totsal);
END;
Frequently Asked Questions :-

What is PL/SQL?

PL/SQL is a procedural language that has both interactive SQL and procedural
programming language constructs such as iteration, conditional branching.

What is the basic structure of PL/SQL?

PL/SQL uses block structure as its basic structure. Anonymous blocks or nested
blocks can be used in PL/SQL.

What are the components of a PL/SQL Block?

Declarative part, Executable part and Exception part.

What are the datatypes available in PL/SQL?

11
Some scalar data types such as NUMBER, VARCHAR2, DATE, CHAR, LONG, BOOLEAN.
Some composite data types such as RECORD & TABLE.

Is there a limit on the size of a PL/SQL block?

Currently, the maximum parsed/compiled size of a PL/SQL block is 64K and the
maximum code size is 100K.

What is the difference between Anonymous blocks and sub programs ?

Anonymous blocks are unnamed blocks which are not stored anywhere while sub
programs are compiled and stored in database. Anonymous blocks compile at run time.

Explain why the following code generates a compilation error.?

Sql>Declare
v_emp_joining date;
Begin
v_emp_joining := ’1 April 1985′;
End;
The given code creates a compilation error because the v_empJoining variable is
declared as the date datatype but has been assigned a character value. To correct the
code, you should use the conversion function, to_date, as follows:

sql>Declare
v_emp_joining date;
Begin
v_emp_joining := to_date(’1 April1985′, ‘DD month yyyy’);
End;
Can SQL group functions be used within the procedural statements?

The SQL group functions, such as AVG, MIN, MAX, COUNT, SUM, STDDEV, and
VARIANCE, cannot be directly used within PL/SQL. However, they can be embedded
within SQL statements and then can be utilized in the procedural statements.

What is the error in the following code?

Sql> Begin
Create table emp_dept(emp_code varchar2(10),dept_code Varchar2(10));
End;
The code will raise a parsing error as the block has a DDL statement, which is not
allowed directly within a PL/SQL block. The DBMS_SQL package should be used to use
a DDL statement.

How is global variable declared within a nested block?

Any variable declared within a block is local to the block and global to the nested
blocks. The variables declared within the nested blocks are unknown to the outer
blocks. They override the references to any outer declared variables with the same
name unless they are used with the block label name.

What is the maximum length of an identifier that can be used?

The maximum length of an identifier in a PL/SQL block is 30.

12
What are the different datatypes that can be defined in a PL/SQL block?
1.Scalar — Holds a single value and does not contain any internal component. Some of
its data types are given as follows:

2. Composite — Contains an internal component, which can be manipulated


individually. It is also known as collections and includes the following data types:
=> TABLE
=> RECORD
=> NESTED TABLE
=> VARRAY
3. Reference — Holds values called pointers, which designate to other objects. It
includes the following data types:
=> REF CURSOR
=> REF object_type
4. LOB — Holds values, which specify the location of large objects, such as images and
video clips. It can store up to 4GB of unstructured data. It includes the following data
types:
=> BFILE
=> BLOB
=> CLOB
=> NCLOB

What is the error in the following code(Given)?


Sql> Begin
select ename from emp where empno=7902;
end;
/
The Select query has been written without the into clause, which is mandatory
for a Select statement in a PL/SQL block.
As a result, the Select query cannot be displayed without assigning it to a
variable.

What is the difference between SQL and PL/SQL?

• Structured Query Language (SQL) is a non-procedural language that interacts


with the database, and is used for database manipulation using the Data Definition
Language (DDL) and Data Manipulation Language (DML statements. Control
statements cannot be used in SQL, which is compiled and executed statement by
statement at the runtime (late-binding).

•PL/SQL is a programming language that allows the usage of Cursor Control


statements and Transaction Control statements, such as if…then…else. It in egrates
with SQL functions and statements to interact and manipulate with the database.
Unlike SQL, PL/SQL statements are compiled and processed as a block of code into the
machine-readable code, which is executed at runtime (early binding); and therefore,
improves the performance.

What is the error in the following code(Below)?

Sql> Declare
vx number;
vy number;
1_result number;

13
2_result number;
Begin
vx :=10;
vy :=20;
1_result :=vx*vy;
2_result :=vx+vy;
End;
/
The variable names in PL/SQL cannot begin, with a numeric character. In PL/SQL,
identifier should begin with an alphabet.

What are the advantages of PL/SQL?

PL/SQL is a transaction processing procedural language that has the following


advantages:
a. Integration with database — PL/SQL supports SQL statements, thus enabling
integration between procedural language capabilities and the database technology.
b. Better performance — SQL statements are grouped within a PL/SQL block and sent
together to database for parsing therefore, it reduces the network traffic and increases
the performance.
c. Higher productivity — PL/SQL helps the package reusable code within the well-
defined modular blocks; and therefore, increases the productivity.
d. Portability — Programs packages and libraries written in PL/SQL can be reused in
different environments.

What is an anonymous block and how is it executed?

•Anonymous blocks, as the name suggests, are PL/SQL blocks that are not given any
name and cannot be stored in the database.
•These blocks can call another blocks but they cannot be called by any other block, as
they do not have a name.
•An anonymous block is executed by either storing the block code in a file or writing
the block code at the SQL prompt.

Choose the correct statement from the following options. ?

A. V_sample Boolean := ‘TRUE’;


B. V_sample Boolean := ‘true’;
C. V_sample Boolean := TRUE;
D. V_sample Boolean;
Both C and D options are correct; whereas, the A and B options are both wrong, as
any variable assigned a value within single quotes is a character string declaration.
Boolean variable can only take TRUE, FALSE, or NULL values.

What are the mandatory keywords in a PL/SQL program block?

The BEGIN and END keywords are mandatory for any PL/SQL block.

What are Transaction Control statements? Can they be used within the
PL/SQL block?

These statements are valid within a PL/SQL block.

How can you display data from a PL/SQL block?

14
An Oracle supplied package called DBMS_OUTPUT is used to display data within a
PL/SQL block.
Control Statements:-

PL/SQL provides the following control structures:

 Conditional Statements
 Iterative Statements
 Sequential Statements
Conditional Statements:-

Syntax:-

1 if then else:-

IF <Condition> THEN
STATEMENTS;
ELSE
STATEMENTS;
END IF;
2 multi if :-

IF <Condition1> THEN
STATEMENTS;
ELSIF <Condition2> THEN
STATEMENTS;
ELSE
STATEMENTS;
END IF;
3 Nested if :-
IF <Condition> THEN
IF <Condition> THEN
STATEMENTS;
ELSE
STATEMENTS;
END IF;
END IF;
Program to Input Empno and Increment Employee Salary as follows

If JOB is CLERK increment SAL by 10%


If JOB is MANAGER increment SAL by 20%
For OTHERS increment SAL by 5%
DECLARE
vEmpno emp.empno%type;

15
vJob emp.job%type;
BEGIN
vEmpno := &empno;
SELECT job INTO vJob
FROM EMP
WHERE EMPNO=vEmpno;
IF vJob=’CLERK’ THEN
UPDATE EMP SET SAL=SAL*1.1 WHERE EMPNO=vEmpno;
ELSIF vJob=’MANAGER’ THEN
UPDATE EMP SET SAL=SAL*1.2 WHERE EMPNO=vEmpno;
ELSE
UPDATE EMP SET SAL=SAL*1.05 WHERE EMPNO=vEmpno;
END IF;
END;
Program to Input Name of the Person and Print First, Mid , Last Names :-

Input :- Sachin Ramesh Tendulkar

Output :- First Name :- Sachin

Middle Name :- Ramesh

Last Name :- Tendulkar

DECLARE
v_name varchar2(60);
v_fname varchar2(20);
v_mname varchar2(20);
v_lname varchar2(20);
BEGIN
v_name := trim('&person_name');
IF instr(v_name,' ') =0 then
v_fname := v_name;
ELSE
v_fname := substr(v_name,1,instr(v_name,' ')-1);
v_lname := substr(v_name,instr(v_name,' ',-1,1)+1);
v_mname := trim(ltrim(rtrim(v_name,v_lname),v_fname));
END IF;
DBMS_OUTPUT.PUT_LINE('First Name :-'||v_fname);
DBMS_OUTPUT.PUT_LINE('Middle Name :-'||v_mname);
DBMS_OUTPUT.PUT_LINE('Last Name :-'||v_lname);
END;
/

16
Pl/sql program to implement Bank Transaction (debit,credit) :-
Create the following two tables and sequence to generate values for TRID
ACCT_MASTER ACCT_TRANS
ACCNO ACNAME BAL TRID TTYPE TDATE TAMT ACCNO
1 Kumar 10000
Sequence :- CREATE SEQUENCE bank_seq
MINVALUE 1
INCREMENT BY 1
MAXVALUE 9999999;
DECLARE
v_accno acct_master.accno%type;
v_ttype acct_trans.ttype%type;
v_amt acct_trans.tamt%type;
v_bal acct_master.bal%type;
v_trid acct_trans.trid%type;
BEGIN
v_accno := &accno;
v_ttype := '&ttype';
v_amt := &amount;
SELECT bal into v_bal FROM acct_master WHERE accno = v_accno;
IF v_ttype='w' and v_amt > v_bal then
DBMS_OUTPUT.PUT_LINE('insufficient funds');
ELSIF v_ttype='w' and v_amt <= v_bal then
update acct_master set bal=bal-v_amt where accno=v_accno;
v_trid := tseq.nextval;
insert into acct_trans(trid,ttype,tamt,accno)
values(v_trid,'w',v_amt,v_accno);
ELSIF v_ttype='d' then
update acct_master set bal=bal+v_amt where accno = v_accno;
v_trid := tseq.nextval;
insert into acct_trans(trid,ttype,tamt,accno)
values(v_trid,'d',v_amt,v_accno);
ELSE
DBMS_OUTPUT.PUT_LINE('invalid transaction');
END IF;
COMMIT;
end;
CASE Statements :-

17
A CASE statement has two forms Simple CASE and Searched CASE. Simple CASE
statement allows you to specify a selector that determines which group of actions to
take. A searched CASE statement does not have a selector it has search conditions
that are evaluated in order to determine which group of actions to take.
Simple CASE statement :-
CASE SELECTOR
WHEN EXPRESSION1 THEN STATEMENT 1;
WHEN EXPRESSION2 THEN STATEMENT 2;
--------------------------------
ELSE
STATEMENT N;
END CASE;
If SELECTOR = EXPRESSION1 then STATEMENT 1 is executed , IF SELECTOR =
EXPRESSION2 then STATEMENT 2 is executed Otherwise ELSE statement is executed.
Example 1 :-
DECLARE
v_date DATE ;
v_day VARCHAR2(1);
BEGIN
v_date := SYSDATE;
v_day := TO_CHAR(v_date,’D’);
CASE v_day
WHEN ‘1’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Sunday’);
WHEN ‘2’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Monday’);
WHEN ‘3’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Tuesday’);
WHEN ‘4’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Wednesday’);
WHEN ‘5’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Thursday’);
WHEN ‘6’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Friday’);
ELSE
DBMS_OUTPUT.PUT_LINE(‘Today is Saturday’);
END CASE;
END;
Example 2:-
Pl/sql program to input empno and increment employee salary as follows

18
if employee deptno=10 increment salary by 10%
if employee deptno=20 increment salary by 15%
if employee deptno=30 incremnet salary by 20%
Others increment salary by 5%
DECLARE
vempno EMP.EMPNO%TYPE;
vdeptno EMP.DEPTNO%TYPE;
BEGIN
vempno := &empno;
SELECT deptno INTO vdeptno
FROM emp
WHERE empno=vempno;
CASE vdeptno
WHEN 10 THEN
UPDATE emp SET sal=sal*1.1 WHERE empno=vempno;
WHEN 20 THEN
UPDATE emp SET sal=sal*1.15 WHERE empno=vempno;
WHEN 30 THEN
UPDATE emp SET sal=sal*1.2 WHERE empno=vempno;
ELSE
UPDATE emp SET sal=sal*1.05 WHERE empno=vempno;
END CASE;
END;
Searched CASE :-
CASE
WHEN COND1 THEN STATEMENT 1;
WHEN COND2 THEN STATEMENT 2
----------------------
ELSE
STATEMENT N;
END CASE ;
If COND1 evaluates to TRUE then STATEMENT 1 is executed if COND2
evaluates to TRUE then STATEMENT 2 is executed otherwise ELSE statement is
executed.
Difference between Simple CASE and Searched CASE :-
In simple CASE statements execution is based on Selector in searched CASE
statements execution is based on Condition. Use simple CASE if condition based on “=”
operator use searched CASE if condition based on other than “=” operator .
Example 1:-
BEGIN

19
CASE
WHEN TO_CHAR(sysdate,’D’)= ‘1’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Sunday’);
WHEN TO_CHAR(sysdate,’D’)= ‘2’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Monday’);
WHEN TO_CHAR(sysdate,’D’)= ‘3’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Tuesday’);
WHEN TO_CHAR(sysdate,’D’)= ‘4’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Wednesday’);
WHEN TO_CHAR(sysdate,’D’)= ‘5’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Thursday’);
WHEN TO_CHAR(sysdate,’D’)= ‘6’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Friday’);
ELSE
DBMS_OUTPUT.PUT_LINE(‘Today is Saturday’);
END CASE;
END;
Example 2:-
STUDENT
SID SNAME S1 S2 S3 STOT SAVG SRES
100 vinod 87 66 89
101 rahul 95 75 30
Write a PL/SQL program to input sid and calculate & update STOT, SAVG, SRES ?
DECLARE
vsid STUDENT.SID%TYPE
vs1 STUDENT.S1%TYPE;
vs2 STUDENT.S2%TYPE;
vs3 STUDENT.S3%TYPE;
vtot STUDENT.STOT%TYPE;
vavg STUDENT.SAVG%TYPE;
vres STUDENT.SRES%TYPE;
BEGIN
vsid := &sid;
SELECT S1,S2,S2 INTO vs1,vs2,vs3
FROM STUDENT
WHERE sid = vsid;
vtot := vs1+vs2+vs3;
vavg := ROUND((vs1+vs2+vs3)/3,2);
CASE
WHEN vs1>=35 AND vs2>=35 AND vs3>=35 THEN

20
vres := ‘PASS’ ;
ELSE
vres := ‘FAIL’ ;
END CASE ;
UPDATE STUDENT SET stot=vtot ,
savg=vavg,
sres=vres
WHERE sid=vsid;
END;
Nested CASE :- a CASE statement can be Nested in another CASE statement as
follows.
CASE
WHEN condition 1 THEN
CASE Selector
WHEN Expression1 THEN
Statement 1;
WHEN Expression2 THEN
Statement 2;
----
ELSE
Statement N;
END CASE;
WHEN condition 2 THEN
---------------
END CASE;
Example 1:-
increment employee salary as follows
if deptno=10 if job=’CLERK’ then increment sal by 10%
if job=’MANAGER’ then increment sal by 20%
for others increment sal by 5%
if deptno=20 if job=’CLERK’ then increment sal by 15%
if job=’MANAGER’ then increment sal by 25%
for others increment sal by 10%
for other depts. Increment sal by 12%
DECLARE
veno EMP.EMPNO%TYPE;
vjob EMP.JOB%TYPE;
vdno EMP.DEPTNO%TYPE;
vincr NUMBER;
BEGIN

21
veno := &eno;
SELECT DEPTNO,JOB INTO vdno,vjob
FROM emp
WHERE empno=veno;
CASE vdno
WHEN 10 THEN
CASE vjob
WHEN 'CLERK' THEN
vincr := 1.1;
WHEN 'MANAGER' THEN
vincr := 1.2;
ELSE
vincr := 1.05;
END CASE;
WHEN 20 THEN
CASE vjob
WHEN 'CLERK' THEN
vincr := 1.15;
WHEN 'MANAGER' THEN
vincr := 1.25;
ELSE
vincr := 1.1;
END CASE;
ELSE
vincr := 1.12;
END CASE;
UPDATE emp SET sal=sal*vincr WHERE empno=veno;
commit;
END;
/

Iterative Statements:-

PL/SQL provides the following three types of Iterative statements

 Simple Loop
 While Loop
 For Loop
Simple Loop:-

A Simple Loop repeats a set of statements in the PL/SQL code block at least once
before the loop terminates. When the EXIT condition is satisfied the process exists
from the loop.

Syntax:-

22
LOOP
<Sequence of statements>
EXIT WHEN <condition>;
END LOOP;
Example:- pl/sql program to print numbers from 1 to 50 ?

DECLARE
X NUMBER(2) := 1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(X);
X := X+1;
EXIT WHEN X>50;
END LOOP;
END;
/
WHILE Loop:-

A While Loop repeats a set of statements in the PL/SQL code block as long as a
condition is true. The condition is evaluated when iteration begins. The iteration
continues until the condition becomes false.

Syntax:-

WHILE <Condition>
LOOP
<STATEMENTS> ;
END LOOP;
A While Loop can be constructed as follows:

1. Initialize a variable before the loop body

2. Increment the variable in the loop

Example 1:-

Write a pl/sql program to input two dates and print number of Sundays between those
two dates.

DECLARE
D1 DATE;
D2 DATE;
CNT NUMBER := 0;
BEGIN
D1 := ‘&DATE1’;
D2 := ‘&DATE2’;

23
D1 := NEXT_DAY(D1-1,’SUNDAY’);
WHILE(D1<=D2)
LOOP
CNT := CNT+1;
D1 := D1+7;
END LOOP;
DBMS_OUTPUT.PUT_LINE(‘ No of Sundays :-‘||CNT);
END;
Input :- DATE1 :- 01-JAN-14

DATE2 :- 31-DEC-14

Output :- 52

FOR Loop:-

A FOR LOOP is used to execute a set of statements for a predetermined number of


times. Iteration occurs between the given start and end integer values. The counter is
always incremented by 1. The loop exits when the counter reaches the value of the end
integer.

The variable in the For Loop need not be declared. Also the increment value cannot be
specified. The For Loop variables is always incremented by 1.

Syntax:-

FOR <variable> IN [REVERSE] <StartValue>..<EndValue>


LOOP
<statements>
END LOOP;
In a For Loop:

1. The counter variable is implicitly declared in the declaration section, so it is not


necessary to declare it explicitly

2. The counter variable is incremented by 1 and does not need to be incremented


explicitly

Example 1 :-

BEGIN
FOR x IN 1..50
LOOP
DBMS_OUTPUT.PUT_LINE(i);
END LOOP;
END;
The above program prints numbers from 1 to 50

24
Example 2 :-

BEGIN
FOR x IN REVERSE 1..50
LOOP
DBMS_OUTPUT.PUT_LINE(i);
END LOOP;
END;
The above program prints numbers from 5O TO 1.
Example 3:-
Write a pl/sql program to input a string and print reverse of that string ?
DECLARE
S1 VARCHAR2(20);
S2 VARCHAR2(20);
L NUMBER;
BEGIN
S1 := ‘&STRING’;
L := LENGTH(S1);
FOR I IN 1..L
LOOP
S2 := S2 || SUBSTR(S1,-I,1);
END LOOP;
DBMS_OUTPUT.PUT_LINE(‘Reverse of that string :- ‘|| S2);
END;
input :- Enter String :- WELCOME

Output :- EMOCLEW

GOTO Statement:-

Often times, it is required to shift the control of the program from a set of statements
to some other set of statements. This means the statements in the code block will not
execute in sequence as it usually does.

The GOTO statement helps achieving this, by allowing shifting the control from one
block of code to some other block of code within a PL/SQL block.

It is an unconditional branch that jumps execution to the label identified in the


statement.

The entry point into such a block of code is marked using the lables:

<<Any name to the code block that follows>>

25
The GOTO statement can then make use of this user-defined name to jump into that
block of code for execution. The label must be in scope when the GOTO statement is
encountered.

Syntax:-

GOTO <CodeBlock Name>;

Using %ROWTYPE in PL/SQL :-

DECLARE
v_eno emp.empno%type;
e emp%rowtype;
BEGIN
v_eno := &eno;
select * into e from emp where empno=v_eno;
dbms_output.put_line(e.empno||' '||e.ename||' '||e.sal);
END;
/

DML and Records :- from Oracle 9i you can use records in INSERT and UPDATE
statements. In INSERT , UPDATE commands you can use records that are based on
%ROWTYPE variable against the table to which INSERT is made.

Example :- Insert a row into emp table with %ROWTYPE record

DECLARE
my_emp emp%ROWTYPE;
BEGIN
my_emp.empno := 1004;
my_emp.ename := ‘ sanjay’;
my_emp.job := ‘CLERK’;
my_emp.mgr := NULL;
my_emp.hiredate := sysdate;
my_emp.sal := 4000;
my_emp.comm := 200;
my_emp.deptno := 10;
INSERT INTO emp VALUES my_emp;
COMMIT;
END;
Notice that you do not include parentheses around the record specifier.If you this
format
INSERT INTO emp VALUES(my_emp) ; INVALID

26
Then you will get an ORA-00947 : not enough values exception.
Record-based updates :- From oracle 9i you also perform updates of an entire row
with a record. The following example Inserts a row into the books table with a
%ROWTYPE record.
Example :-

DECLARE
my_dept dept%ROWTYPE;
BEGIN
my_dept.deptno := 10;
my_dept.dname := ‘ACCOUNTING’
my_dept.loc := ‘LONDON’;
UPDATE dept
SET ROW=my_dept
WHERE deptno = my_dept.deptno;
COMMIT;
END;
Notice that i used a new keyword , ROW, to indicate that I am updating entire
row with a record.

Frequently Asked Questions :-

How can you force FOR-LOOP to end unconditionally?

The EXIT statement can be used to end a FOR-LOOP unconditionally, as shown in the
following code snippet:

Explain the DECODE function? Can it be used in the procedural statements?


The DECODE function compares its first argument to one or more search expressions
that are paired with result expressions. Any of the search or result expression can be
null. If a search is successful, the corresponding result is returned.

No, the DECODE function cannot be used within the procedural statements. It can only
be used within the SQL statements, as shown in the following code:

SELECT DECODE(emp_rating, NULL, 1500. ‘C’, 4000 ‘B’, 6000, ‘A’, 8000)

INTO var_bonus FROM t_emp_rating WHERE emp_code = var_emp_code;

In the preceding code, if the emp_rating column is null, the DECODE function returns
the value 1500.

What is the equivalent of DECODE function in the procedural statements?

The if…then…else statement is the equivalent of DECODE function within the


procedural statements.

What is the difference between EXIT and EXIT-WHEN statements?

27
The EXIT statement is used to force a loop to terminate unconditionally. When an
EXIT statement is encountered within a loop, the loop is completed and the control is
passed to the next statement after the loop. Following is the syntax of the EXIT
statement:

LOOP

…..

IF condition THEN

Statements for Processing ….

EXIT;

END IF;

END LOOP;

The EXIT-WHEN statement terminates a loop when the given condition is fulfilled.
When the EXIT statement is encountered, first the condition is checked. If the condition
is TRUE, then the loop is completed and the control is passed to the statement after
the loop. Following is the syntax of the EXIT-WHEN statement:

LOOP

…..

IF condition THEN

Statements for Processing ….

EXIT WHEN condition….

END IF;

END LOOP;

Distinguish between WHILE-LOOP and FOR-LOOP.?

The WHILE-LOOP associates a condition with a loop. First, the condition is checked
and then the loop is started. The loop continues until the condition remains TRUE. If
the condition becomes FALSE, the loop is completed.

The number of iterations is not known as it is based only on a condition. The loop may
not be executed at all, as the condition is checked at the start of the loop itself.

In a FOR-LOOP, can the counter value be assigned a value within the loop?

The counter of a FOR-LOOP can only be used as a constant within the loop.

It cannot be assigned a value inside the loop, as that would change the bounds of the
loop, which is logically not possible.

Cursors :-

28
 In order for Oracle to process an SQL statement, it needs to create an area of
memory known as the context area also called Active Data Set. A cursor is a pointer to
the context area. Through the cursor , a PL/SQL program can control the context area.

The data that the query retrieves from table(s) is held in a cursor opened in the
memory by Oracle. This data is then transferred to the client machine when demanded
it.

When a cursor is declared, a pointer is returned which initially point to nothing. When
the cursor is opened, appropriate memory is allocated and the cursor structure is
created. The cursor variable now points to the cursor area in the memory. When the
cursor is closed the memory allocated for the cursor is released.

Cursors are usually used to hold the data that is retrieved from table(s) and perform
actions on that data one row at a time.

Types Of Cursors:-

Oracle provides the following two kinds of cursors.

 Explicit Cursor

 Implicit Cursor

Explicit Cursor:-

A cursor that is explicitly opened for processing data using a PL/SQL block is known as
an Explicit Cursor. An explicit cursor is useful, when it is required to process individual
records (row-by-row) from a database table. An Explicit Cursor is declared in the
DECLARE section of a PL/SQL program.Explicit cursors are used in queries that return
multiple rows. Processing multiple rows is very similar to processing a flat file.

A flat file can be processed as:

 Open a file
 Process records
 Close the file
Similarly, an explicit cursor can be processed as:

 Declare a cursor mapped to an SQL SELECT query


 Open the cursor
 Fetch records/ rows from the cursor one row at a time into memory
variables
 Process the data held in the memory variables as required using a loop
 Exit from the loop after processing is complete
 Close the cursor

29
Now that the cursor is declared, the next logic step is to use it. A cursor can be put to
use, by opening, fetching the data and finally closing it when not required.

Declaring Cursor:-

CURSOR <CursorName> IS <SELECT Query>

Example:-

DECLARE

CURSOR curEmp IS SELECT ename,sal FROM emp ;

BEGIN

END;

Opening Cursor:-

A cursor after it is declared needs to be opened.

Syntax:-

OPEN <CursorName>;

Example:-

OPEN C1;

The following events occur when cursor is opened :-

 Defines a named portion in the memory


 Executes the associated SQL query
 Creates the Active Data Set by retrieving data and populating the
named portion of the memory
 Establishes a row pointer and sets it to point to the first row in the
Active Data Set

Fetching Data From Cursor:-

Now since the data is available in the cursor [Active Data Set], to manipulate it, it
needs to be fetched into a set of memory variables. The FETCH command allows
moving such data into memory variables. It retrieves one row at a time.

Syntax:-

FETCH <CursorName> INTO <Variable1> , <Variable2>,……;

Example :-

FETCH c1 INTO varEname,varSal;

The FETCH command can be placed inside a Loop….End Loop construct, which causes
the data to be fetched into memory variables and processed until all the rows in the
Active Data Set are processed.

30
Closing Cursor:-

After the data is fetched from the Active Data Set, the cursor needs to be closed. The
CLOSE command helps achieving this. Closing a cursor releases the memory occupied
by the cursor and it’s Active Data Set.

Syntax:-

CLOSE<CursorName>;

Example :-

CLOSE C1;

After a cursor is closed, it can be opened again using the open command.

Below figure explains how cursor works.

Cursor Attributes:-

Oracle provides four variables, which help keep track of the current status of a cursor.
These cursor variables can be accessed and used in a PL/SQL code block.An attribute
can be used by preceding the cursor attribute with the cursor name.

%FOUND:-

%FOUND evaluates to TRUE, if the record was fetched successfully. Otherwise,


evaluates to FALSE. If the cursor has not been opened, a reference to the %FOUND
attribute raises the INVALID_CURSOR exception.

%NOTFOUND:-

%NOTFOUND evaluates to TRUE, if the record was not fetched successfully. Otherwise,
evaluates to FALSE. If the cursor has not been opened, a reference to the
%NOTFOUND attribute raises the INVALID_CURSOR exception.

%ROWCOUNT:-

%ROWCOUNT returns the number of rows fetched from a cursor at the time this
attribute is queried. If the cursor has not been opened, a reference to the
%ROWCOUNT attribute raises the INVALID_CURSOR exception.

31
%ISOPEN :-

Returns TRUE if cursor is opened otherwise returns FALSE.

All the above attributes are references with CURSOR name

C1%FOUND C1%ROWCOUNT

C1%NOTFOUND C1%ISOPEN

Example :-

Pl/sql progrma to Display all employee names and their salaries ?

Using simple loop:-

DECLARE
CURSOR curEmp IS
SELECT ename,sal FROM emp;
vename emp.ename%type;
vsal emp.sal%type;
BEGIN
OPEN curEmp;
LOOP
FETCH curEmp INTO vename,vsal;
EXIT WHEN curEmp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(vename||’ ‘||vsal);
END LOOP;
CLOSE curEmp;
END;
/
Using %ROWTYPE :-

DECLARE
CURSOR curEmp IS
SELECT ename,sal FROM emp;
r curEmp%ROWTYPE;
BEGIN
OPEN curEmp;
LOOP
FETCH curEmp INTO r;
EXIT WHEN curEmp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(r.ename||’ ‘||r.sal);
END LOOP;
CLOSE curEmp;

32
END;
/
Note :- %ROWTYPE variable reduces no of simple variables required in a
program.
Example :-

A pl/sql program to display first 5 salaries from emp table ?

DECLARE
CURSOR C1 IS SELECT DISTINCT SAL FROM EMP ORDER BY SAL DESC;
r C1%ROWTYPE;
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO r;
EXIT WHEN C1%ROWCOUNT>5 OR C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(r.SAL);
END LOOP;
CLOSE C1;
END;
/
In the above program C1%ROWCOUNT>5 will be true if table contains more than 5
rows and C1%NOTFOUND will be true if table contains less than 5 rows.

Example : a pl/sql cursor program display even number of records from emp table
using %rowcount attribute?

DECLARE
CURSOR C1 IS SELECT ENAME, SAL FROM EMP;
V_ENAME VARCHAR2(20);
V_SAL NUMBER(10);
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO V_ENAME, V_SAL;
EXIT WHEN C1%NOTFOUND;
IF MOD(C1%ROWCOUNT,2)=0 THEN
DBMS_OUTPUT.PUT_LINE(V_ENAME||' ' ||V_SAL);
END IF;
END LOOP;
CLOSE C1;
END;
Example :- a PL/SQL program to check whether a record exists in table or not using
cursor.

DECLARE
cursor c1 is SELECT 'X' FROM emp WHERE empno=7844;

33
s varchar2(1);
BEGIN
OPEN C1;
FETCH C1 INTO s;
IF C1%FOUND THEN
DBMS_OUTPUT.PUT_LINE('Employee exists');
ELSE
DBMS_OUTPUT.PUT_LINE('No Such Employee');
END IF;
END;
/
Using While Loop:-

DECLARE
CURSOR C1 IS SELECT ename,sal FROM emp;
Vename emp.ename%type;
Vsal emp.sal%type;
BEGIN
OPEN C1;
FETCH C1 INTO vename,vsal;
WHILE C1%FOUND
LOOP
DBMS_OUTPUT.PUT_LINE(vename||’ ‘||vsal);
FETCH C1 INTO vename,vsal;
END LOOP;
CLOSE C1;
END;

Cursor FOR Loop:-

Cursor FOR Loop which helps processing Active Dataset without


OPENING,FETCHING,CLOSING CURSOR.

A FOR Loop implicitly:

 declares a %ROWTYPE variable, also uses it as LOOP index


 Opens a cursor
 fetches records from Active Dataset
 loads record into Loop variable
 Closes the cursor automatically when all records have been
processed.
Hence the commands such as OPEN, FETCH and CLOSE are not required here.

Syntax:-

34
FOR <Variable> IN <CursorName>
LOOP
Statements;
END LOOP;
The above loop is executed number of times number of records are there in cursor.
Every time for loop is executed a record is fetched from cursor and the record is
assigned to ROWTYPE variable (loop index).

Example:-

DECLARE
CURSOR C1 IS SELECT ename,sal FROM emp;
BEGIN
FOR r IN C1
LOOP
DBMS_OUTPUT.PUT_LINE(r.ename||’ ‘||r.sal);
END LOOP;
END;
/
Example :-

Write a program using PL/SQL that Fetches employee records in memory

Generates Username and Password for each employee that is fetched

Updates the Username and Password column of the Employees table with the
generated values.

The username should be formed as:

FirstName LastName EmployeeNo Username

Sharanam Shah 1 AMSH1

The password should be formed as:

FirstName LastName Salary Password

Sharanam Shah 35232 SHSH32

DECLARE
CURSOR curEmp IS
SELECT EmployeeNo, FirstName, LastName, Salary FROM Employees;
varUname Employees.Username%TYPE;
varPwd Employees.Password%TYPE;
BEGIN
FOR r IN curEmp
LOOP

35
varUname :=SUBSTR(r.FirstName, -2, 2)
||SUBSTR(r.LastName, 1, 2)|| r.EmployeesNo;
varPwd :=SUBSTR(r.FirstName, 1, 2)
||SUBSTR(r.LastName, 1, 2)
||SUBSTR(TO_CHAR(r.Salary), -2, 2);
UPDATE Employees SET Username = varUname,
Password = varPwd
WHERE EmployeeNo = r.EmployeeNo;
END LOOP;
COMMIT;
END;
Defining An Inline Cursor:-

The FOR loop, allows defining an inline cursor. An inline cursor is a cursor that is not
declared in the DECLARE section but the cursor definition is included in the FOR loop.

Example:-

BEGIN
FOR r in (SELECT ename,sal FROM emp)
LOOP
DBMS_OUTPUT.PUT_LINE(r.ename||’ ‘||r.sal);
END LOOP;
END;
Parameterized Cursor:-

Often times, it is required to have a generic SQL query that retrieves data based on a
parameter it receives. SQL queries allows this using a WHERE clause.

Similarly, the cursor that uses an SQL query to retrieve the required data can be
passed a parameter which in turn can be passed to the WHERE condition of the actual
query.

A cursor that accepts parameters is called a parameterized cursor. The contents of


such a cursor changes depending upon the value passed to its parameter.

Declaring Cursor:-

Syntax:-

CURSOR <CursorName> (VariableName> <Datatype>) IS

<SELECT statement . . . . >

Opening Cursor:-

Syntax:-

36
OPEN <CursorName> (<Value> | <variable> | <Expression>);

FOR <variableName>

IN <CursorName> (<value> | <variable> | <Expression>)

Example:-

DECLARE
CURSOR C1(d number)
is SELECT ENAME,SAL FROM EMP WHERE DEPTNO=d;
BEGIN
FOR e in C1(10)
LOOP
DBMS_OUTPUT.PUT_LINE(e.ENAME||e.SAL);
END LOOP;
END;
Example:-

DECLARE
CURSOR C1(d number,j varchar2) is
SELECT ENAME,SAL,JOB,DEPTNO FROM EMP
WHERE DEPTNO=d AND job=j;
BEGIN
FOR e in C1(&dno,’&job’);
LOOP
DBMS_OUTPUT.PUT_LINE(e.ENAME||e.SAL||e.JOB||e.DEPTNO);
END LOOP;
END;
/
Cursor Within Cursor Example :-

The company holds Employees and Departments details in Master tables called Emp
and Dept. For reporting purposes, it is required to store the information from these
tables in a denormalized table EmpDept.

Write a program using PL/SQL that:

Fetches departments from the Departments table For every department, fetche
employees belonging to that department from the Employees table and Add all the
information from both the tables to the EmpDept table

DECLARE
CURSOR c1 IS SELECT * FROM Dept;
CURSOR c2(d Dept.DeptNo%TYPE)
IS SELECT * FROM Emp WHERE DeptNo =d;
BEGIN
FOR d IN c1

37
LOOP
FOR e IN c2(d.deptno)
LOOP
INSERT INTO EmpDept(EmpNo,Ename,Sal,Deptno,Dname,Loc)
VALUES (e.EmpNo,e.Ename,e.Sal,d1.Deptno,d1.Dname,d1.Loc)
END LOOP;
END LOOP;
COMMIT;
END;
Example :- a PL/SQL program that displays list of table names and displays list of
columns belongs to that table.

BEGIN
FOR t IN (SELECT TABLE_NAME FROM USER_TABLES)
LOOP
DBMS_OUTPUT.PUT_LINE(t.TABLE_NAME);
FOR c IN (SELECT COLUMN_NAME FROM USER_TAB_COLUMNS
WHERE TABLE_NAME=t.TABLE_NAME);
LOOP
DBMS_OUTPUT.PUT_LINE(c.COLUMN_NAME);
END LOOP;
END LOOP;
END;
Locking Cursor Data:-

When a SELECT query is fired to retrieve data, no locks are placed on the selected
rows. It is required, to lock a set of records before the changes are applied by a
program using PL/SQL. Oracle provides FOR UPDATE clause that can be used with a
SELECT statement to perform this locking.

If the FOR UPDATE clause is used with SELECT query, Oracle obtains an exclusive row-
level lock on all the rows identified by the SELECT statement. On applying an exclusive
lock, no one else will be able to change any of the records until a ROLL BACK or a
COMMIT is fired.

Syntax :-

1 CURSOR C1 IS SELECT * FROM EMP FOR UPDATE;

When we declare a CURSOR FOR UPDATE ,

 each row is locked as we open the CURSOR


 This prevents other users modifying row while our cursor is open
 It allows us to modify rows using WHERE CURRENT OF clause
 This does not prevent other users from reading the rows

38
2 CURSOR C1 IS SELECT * FROM EMP FOR UPDATE OF SAL NOWAIT;

If the rows have been already locked by another session then NOWAIT option
returns error.

3 CURSOR C1 IS SELECT * FROM EMP FOR UPDATE OF SAL wait 5 ;

Wait n Waits for n seconds and returns error if other session still locking
the rows at the end of the time.

4 if cursor is based on a join of two tables , we may want to lock the rows of
one table but not the other. To do this we specify any column of that table.for
example

CURSOR c1 IS SELECT e.empno,e.ename,d.dname


FROM emp e, dept d
WHERE e.deptno=d.deptno
AND d.dname=’SALES’ FOR UPDATE OF SAL;
Example :-

DECLARE
CURSOR c1 IS SELECT * FROM EMP
FOR UPDATE NOWAIT;
BEGIN
FOR e IN C1
LOOP
UPDATE EMP SET SAL=SAL*1.1 WHERE EMPNO=e.EMPNO;
END LOOP;
COMMIT;
END;
CURRENT OF clause :-
Used to access current row of a cursor current of clause used in update, delete
statements only. Whenever we are using a where current of clause we must use for
update clause .after processing the records we must release the locks using commit.
consider a table with two columns ENAME,SAL
EMP
ENAME SAL
A 5000
B 6000
A 4000
B 5000
C 3000
A Pl/sql program to increment each employee salary by 1000
DECLARE

39
CURSOR C1 IS SELECT * FROM EMP
FOR UPDATE OF SAL NOWAIT;
BEGIN
FOR r IN C1
LOOP
UPDATE EMP SET SAL=SAL+1000 WHERE ENAME=r.ENAME;
END LOOP;
COMMIT;
END;
After executing the above program A,B salaries are increment by 2000 because of the
duplicate values so to overcome the above problem use CURRENT OF clause in WHERE
Condition as follows
DECLARE
CURSOR C1 IS SELECT * FROM EMP
FOR UPDATE OF SAL NOWAIT;
BEGIN
FOR r IN C1
LOOP
UPDATE EMP SET SAL=SAL+1000 WHERE CURRENT OF C1;
END LOOP;
COMMIT;
END;
/
Example:-Write a pl/sql program to update F3 as follows

T1

F1 F2 F3
5 4 F1+F2
5 4 F1-F2
5 4 F1*F2
5 4 F1/F2
DECLARE
CURSOR C1 IS SELECT * FROM T1 FOR UPDATE OF F3;
BEGIN
FOR I IN C1
LOOP
IF (C1%ROWCOUNT=1) THEN
UPDATE T1 SET F3=F1+F2 WHERE CURRENT OF C1;
ELSIF (C1%ROWCOUNT=2) THEN

40
UPDATE T1 SET F3=F1-F2 WHERE CURRENT OF C1;
ELSIF (C1%ROWCOUNT=3) THEN
UPDATE T1 SET F3=F1*F2 WHERE CURRENT OF C1;
ELSE
UPDATE T1 SET F3=F1/F2 WHERE CURRENT OF C1;
END IF;
COMMIT;
END;
CURSOR Variables (REF cursor):-

A cursor variable differs from cursors the way constants differs from variables. A
cursor is static , a cursor variable is dynamic. Cursors always points to same work
area,while cursor variable can point to different work areas.

You can use a cursor variable to pass result set of a query between stored
procedures.

There are two types of cursor variable.

1 WEAK

2 STRONG

Weak REF Cursor:-

A REF CURSOR declared without RETURN type is called WEAK REF CURSOR.
PL/SQL provides a built-in WEAK REF CURSOR called SYS_REFCURSOR.
Example :-

DECLARE
C1 SYS_REFCURSOR;
E EMP%ROWTYPE;
BEGIN
OPEN C1 FOR SELECT * FROM EMP;
LOOP
FETCH C1 INTO E;
EXIT WHEN C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(E.ENAME||E.SAL);
END LOOP;
END;
Strong REF Cursor:-

A REF CURSOR declared with RETURN type is called STRONG REF CURSOR. In pl/sql
there is a REF CURSOR datatype , where REF stands for reference and CURSOR stands
for the class of the object.

41
To create a cursor variable

1 REF CURSOR type and then

2 Declare a variable of that type.

Declaring a REF CURSOR Type:-

TYPE <NAME> IS REF CURSOR RETURN <TYPE> ;

TYPE REFTYPE IS REF CURSOR RETURN EMP%ROWTYPE;

Declaring a variable of REF CURSOR TYPE :-

Variablename datatype ;

Example :-

DECLARE
TYPE REFTYPE IS REF CURSOR RETURN EMP%ROWTYPE;
C1 REFTYPE;
E EMP%ROWTYPE;
BEGIN
OPEN C1 FOR SELECT * FROM EMP;
LOOP
FETCH C1 INTO E;
EXIT WHEN C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(E.ENAME||E.SAL);
END LOOP;
END;
Implicit cursor:-

If Oracle opens a cursor for its internal processing it is known as an Implicit Cursor.

The name of the implicit cursor will always be “SQL”. The values of the cursor
attributes always refer to the most recently executed SQL statement, wherever the
statement appears.

Oracle implicitly declares cursors for all DML statements written in PL/SQL. Since the
implicit cursor is declared, opened and managed by Oracle internally, following
functions are taken care by Oracle:

 Reserving a portion of memory

 Populating this portion with appropriate data

 Processing the data in the memory area

 Releasing the memory area when the processing is complete

 it is passed to the client who demanded it.

42
For implicit cursor, Oracle provides attributes that can be used to access status
information such as:

Last Insert
Last Update
Last Delete
Last Single-Row select statements
Attributes:-

%FOUND :-

Returns TRUE if last SQL statement affects a row otherwise returns FALSE.

%NOTFOUND :-

Returns TRUE if last SQL statement doesn’t affects a row otherwise returns a FALSE.

%ROWCOUNT :-

Returns no of records affected by the last SQL statement.

Example:-

The company desires to transfer a few employees across the available departments.

Write a program using PL/SQL that:

Accepts the EmployeeNo of the employee to be transferred

Accepts the new DeptNo of that employee

Updates the Department number of that employee to the new DeptNo

After the employee has been successfully transferred, indicate using a message.

DECLARE
vempno Emp.EmpNo%TYPE;
vdeptno Emp.DeptNo%TYPE;
BEGIN
vempno := &empno;
vdeptno := &DeptNo;
UPDATE Emp SET DeptNo = vdeptno WHERE EmpNo = vempno;
IF SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE(‘Employee’||varEmployeeNo||
’Successfully Transferred to DeptNo:’ ||varDeptNo);
END IF;
END;

Let us take another example where exactly implicit cursor use useful. For example
consider a table which stores customer details opens account in bank

43
ACCT_MASTER

ACCNO ACNAME BAL

100 SACHIN 10000

101 KOHLI 5000

I want to implement a transaction i.e. transferring amount from one account to


another account . when implementing transaction , the transaction should support

A property called atomocity i.e. either all operations must be done or none must be
done. If individual operations are successful then commit the transaction and if any of

Operation is failed then cancel the whole transaction.

DECLARE
V_SACCNO CUSTOMER.ACCNO%TYPE;
V_TACCNO CUSTOMER.ACCNO%TYPE;
V_AMT CUSTOMER.BAL%TYPE;
CNT1 NUMBER;
CNT2 NUMBER;
BEGIN
V_SACCNO := &SACCNO;
V_TACCNO := &TACCNO;
V_AMT := &AMOUNT;
UPDATE CUSTOMER SET BAL=BAL-V_AMT WHERE ACCNO=V_SACCNO;
CNT1 := SQL%ROWCOUNT;
UPDATE CUSTOMER SET BAL=BAL+V_AMT WHERE ACCNO=V_TACCNO;
CNT2 := SQL%ROWCOUNT;
IF (CNT1=1 AND CNT2=1) THEN
COMMIT;
ELSE
ROLLBACK;
END IF;
END;
Frequently asked questions:-

What is a cursor? Why Cursor is required?

Cursor is a named private SQL area from where information can be accessed.
Cursors are required to process rows individually for queries returning multiple rows.
Explain the two types of Cursors?
There are two types of cursors
Implict Cursor
Explicit Cursor

44
Explain How CURSOR FOR LOOP works ?
A cursor FOR loop 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.
What are the PL/SQL Statements used in cursor processing?
DECLARE CURSOR cursor name,
OPEN cursor name,
FETCH cursor name INTO <variable list> or Record types,
CLOSE cursor name.
What are the cursor attributes used in PL/SQL?
%ISOPEN - to check whether cursor is open or not
% ROWCOUNT - number of rows fetched/updated/deleted.
% FOUND - to check whether cursor has fetched any row. True if rows are fetched.
% NOT FOUND - to check whether cursor has fetched any row. True if no rows are
Fetched.
What is WHERE CURRENT OF clause?
PL/SQL provides the WHERE CURRENT OF clause for both UPDATE and DELETE
statements inside a cursor.This allows you to easily make changes to the most recently
fetched row of data. 
Can we pass parameter to a cursor what are the advantages ?
PL/SQL also allows you to pass parameters into cursors. It eases your work because:
- A parameter makes the cursor more reusable. 
- A parameter avoids scoping problems.
How can you find a cursor is opened or not in PL/SQL block ?
Using %ISOPEN attribute

What is a cursor for loop?


Cursor for loop implicitly declares %ROWTYPE as loop index,opens a cursor, fetches
rows of values from active set into fields in the record and closes when all the records
have been processed.

Can you pass a parameter to a cursor ?


yes

Can you use %RowCount as a parameter to a cursor ?


Yes

What is implicit cursor in Oracle?

A session contains a single implicit cursor which is defined automatically by PL/SQL.


The cursor gets assigned to represent the execution of a statement whenever it is
executed.

What are the Implicit Cursor Attributes ?

45
SQL%FOUND
SQL%NOTFOUND
SQL%ROWCOUNT
SQL%ISOPEN

How is it possible to fetch a current row from a cursor without using ROWID?

The WHERE CURRENT OF clause is used to reference the current row of an active
explicit cursor. When this clause is used, ROWID is not needed to access the current
row for modifications.

How can you check if an UPDATE statement in PL/SQL is successfully


executed?

You can use the SQL%NOTFOUND attribute to check if the UPDATE statement has
successfully updated any rows or not. This attribute returns the TRUE value if the last
executed SQL statement has not affected any rows.

What is the value of SQL%FOUND attribute after and before an INSERT


statement is executed?

The value of SQL%F0UND attribute will be NULL before an INSERT statement is


executed and TRUE after the successful insertion into a table.

What are the different ways to determine the status of a cursor?

The status of a cursor can be determined by checking the attributes of that cursor.
Following are the attributes of a cursor:
=> %FOUND
=> %NOTFOUND
=> %ISOPEN
=> %ROWCOUNT
What will be the value of %N0TF0UND attribute after a cursor is opened but
before a FETCH statement is executed?

The value of %NOTFOUND attribute will be NULL after the cursor is opened but before
the FETCH statement is executed. After the FETCH statement, the value of
%NOTFOUND attribute will be TRUE OR FALSE depending on the result of the FETCH
statement.
Why does SQL%ISOPEN always returns the FALSE value?

SQL%ISOPEN always evaluates to FALSE as ORACLE closes the implicit cursor as soon
as it executes the query.

How can explicit cursors be used to fetch multiple rows?

A loop must be defined to fetch multiple rows from an explicit cursor. It will fetch one row
from the active cursor set on each iteration of the loop.
Collections and Records :-
In a collection, the internal components are always of the same data type, and are
called elements. You access each element by its unique subscript. Lists and arrays are
classic examples of collections.

46
In a record, the internal components can be of different data types, and are
called fields. You access each field by its name. A record variable can hold a table row,
or some columns from a table row. Each record field corresponds to a table column.
The following are the Different Collections in PL/SQL
1 Associative array (Index by Table)
2 Variable Array
3 Nested Table
Characterstics of PL/SQL collections :-

Unbounded means that, theoretically, there is no limit to the number of elements in the


collection.

Dense means that the collection has no gaps between elements—every element between
the first and last element is defined and has a value.
Associative Arrays or Index By Tables :-

An associative array (also called an index-by table) is a set of key-value pairs. Each
key is unique, and is used to locate the corresponding value. The key can be either an
integer or a string. To use Index by Table in PL/SQL block

1 Declare Index By Table Type

2 Declare Variable of that Type

Declaring PL/SQL Index By Table type :-

TYPE <TYPE_NAME> IS TABLE OF <ELEMENT_TYPE>


INDEX BY <Integer> | <varchar2> ;
Example:-

TYPE arrtype IS TABLE OF NUMBER(4) INDEX BY BINARY_INTEGER;


the above statement declares a datatype called arrtype which allows table of elements
and each element is a number type and elements are accessed by using INDEX value.

47
Declaring a Variable of PL/SQL table type:-

TABLE_NAME TYPE_NAME ;

Example:-

X arrtype ;

X is a PL/SQL table , arrtype is a PL/SQL table type.

Example 1 :-

DECLARE
TYPE arrtype IS TABLE OF number(3)
INDEX BY BINARY_INTEGER;
X arrtype;
BEGIN
FOR I IN 1..10
LOOP
X(I) := I*10;
END LOOP;
FOR I IN 1..10
LOOP
DBMS_OUTPUT.PUT_LINE(X(I));
END LOOP;
END;
Understanding Nested Tables:-

Conceptually, a nested table is like a one-dimensional array with an arbitrary number


of elements. Within the database, a nested table is a column type that holds a set of
values. The database stores the rows of a nested table in no particular order. When
you retrieve a nested table from the database into a PL/SQL variable, the rows are
given consecutive subscripts starting at 1. These subscripts give you array-like access
to individual rows.

A nested table differs from an array in these important ways:

 An array has a declared number of elements, but a nested table does not. The
size of a nested table can increase dynamically.
 An array is always dense (that is, it always has consecutive subscripts). A
nested array is dense initially, but it can become sparse, because you can
delete elements from it.

Understanding Variable-Size Arrays (Varrays) :-

A variable-size array (varray) is an item of the data type VARRAY. A varray has a


maximum size, which you specify in its type definition. A varray can contain a varying
number of elements, from zero (when empty) to the maximum size. A varray index has
a fixed lower bound of 1 and an extensible upper bound. To access an element of a
varray, you use standard subscripting syntax.

48
The below figure shows a varray named Grades, which has maximum size 10 and
contains seven elements. The current upper bound for Grades is 7, but you can
increase it to the maximum of 10. Grades(n) references the nth element of Grades.

Varray of Size 10

Choosing Between Nested Tables and Associative Arrays :-

Nested tables and associative arrays differ in persistence and ease of parameter
passing.

A nested table can be stored in a database column; therefore, you can use a nested
table to simplify SQL operations in which you join a single-column table with a larger
table. An associative array cannot be stored in the database.

An associative array is appropriate for the following:

 A relatively small lookup table, where the collection can be constructed in


memory each time a subprogram is invoked or a package is initialized
 Passing collections to and from the database server.

 PL/SQL automatically converts between host arrays and associative arrays that
use numeric key values. The most efficient way to pass collections to and from
the database server is to set up data values in associative arrays, and then use
those associative arrays with bulk constructs (the FORALLstatement
or BULK COLLECT clause).

Choosing Between Nested Tables and Varrays:-

Varrays are a good choice when:-

 The number of elements is known in advance.


 The elements are usually accessed sequentially.

When stored in the database, varrays keep their ordering and subscripts.

A varray is stored as a single object. If a varray is less than 4 KB, it is stored inside the
table of which it is a column; otherwise, it is stored outside the table but in the same
tablespace.

You must store or retrieve all elements of a varray at the same time, which is
appropriate when operating on all the elements at once. However, this might be
impractical for large numbers of elements.

Nested tables are a good choice when:-

 Index values are not consecutive.


 There is no set number of index values.

49
 You must delete or update some elements, but not all elements at once.

 You would create a separate lookup table, with multiple entries for each row of
the main table, and access it through join queries.

Nested table data is stored in a separate store table, a system-generated database


table. When you access a nested table, the database joins the nested table with its
store table. This makes nested tables suitable for queries and updates that only affect
some elements of the collection.

You cannot rely on the order and subscripts of a nested table remaining stable as the
nested table is stored in and retrieved from the database, because the order and
subscripts are not preserved in the database.

Defining Collection Types :-

You can define TABLE and VARRAY types in the declarative part of any PL/SQL block,
subprogram, or package using a TYPE definition.
For nested tables and varrays declared within PL/SQL, the element type of the table or
varray can be any PL/SQL data type except REF CURSOR.
When defining a VARRAY type, you must specify its maximum size with a positive
integer. In the following example, you define a type that stores up to 366 dates:
DECLARE
TYPE Calendar IS VARRAY(366) OF DATE;
Associative arrays let you insert elements using arbitrary key values. The keys need
not be consecutive.
The key data type can be PLS_INTEGER, VARCHAR2.
Declaring an Associative Array:
DECLARE
TYPE EmpTabTyp IS TABLE OF employees%ROWTYPE
INDEX BY PLS_INTEGER;
emp_tab EmpTabTyp;
BEGIN
/* Retrieve employee record. */
SELECT * INTO emp_tab(100) FROM employees
WHERE employee_id = 100;
END;
/
Declaring Nested Tables, Varrays, and Associative Arrays :-
DECLARE
TYPE nested_type IS TABLE OF VARCHAR2(30);
TYPE varray_type IS VARRAY(5) OF INTEGER;
TYPE assoc_array_num_type
IS TABLE OF NUMBER INDEX BY PLS_INTEGER;

50
TYPE assoc_array_str_type
IS TABLE OF VARCHAR2(32) INDEX BY PLS_INTEGER;
TYPE assoc_array_str_type2
IS TABLE OF VARCHAR2(32) INDEX BY VARCHAR2(64);
v1 nested_type;
v2 varray_type;
v3 assoc_array_num_type;
v4 assoc_array_str_type;
v5 assoc_array_str_type2;
BEGIN
-- an arbitrary number of strings can be inserted into v1
v1 := nested_type('Shipping','Sales','Finance','Payroll');
v2 := varray_type(1, 2, 3, 4, 5); -- Up to 5 integers
v3(99) := 10; -- Just start assigning to elements
v3(7) := 100; -- Subscripts can be any integer values
v4(42) := 'Smith'; -- Just start assigning to elements
v4(54) := 'Jones'; -- Subscripts can be any integer values
v5('Canada') := 'North America';
-- Just start assigning to elements
v5('Greece') := 'Europe';
-- Subscripts can be string values
END;
Specifying Collection Element Types with %TYPE and %ROWTYPE:-
DECLARE
-- Nested table type that can hold an arbitrary number of employee IDs.
-- The element type is based on a column from the EMPLOYEES table.
-- You need not know whether the ID is a number or a string.
TYPE EmpList IS TABLE OF emp.empno%TYPE;
-- Declare a cursor to select a subset of columns.
CURSOR c1 IS SELECT emp FROM emp;
-- Declare an Array type that can hold information about 10 employees.
-- The element type is a record that contains all the same fields as the
EMPLOYEES table.
TYPE Senior_Salespeople IS VARRAY(10) OF emp%ROWTYPE;
-- Declare a cursor to select a subset of columns.
CURSOR c2 IS SELECT ename,sal FROM emp;
-- Array type that can hold a list of names. The element type is a record that
contains the same fields as the cursor (that is ename,sal).
TYPE NameList IS VARRAY(20) OF c2%ROWTYPE;

51
BEGIN
NULL;
END;
/
Declaring NOT NULL Constraint on Collection Elements:-
DECLARE
TYPE EmpList
IS TABLE OF emp.empno%TYPE NOT NULL;
vemp EmpList := EmpList(100, 150, 160, 200);
BEGIN
v_employees(3) := NULL; -- assigning NULL raises an exception
END;
/
Data Type Compatibility for Collection Assignment:-
DECLARE
TYPE last_name_typ IS VARRAY(3) OF VARCHAR2(64);
TYPE surname_typ IS VARRAY(3) OF VARCHAR2(64);
-- These first two variables have the same data type.
group1 last_name_typ := last_name_typ('Jones','Wong','Marceau');
group2 last_name_typ := last_name_typ('Klein','Patsos','Singh');
-- This third variable has a similar declaration, but is not the same type.
group3 surname_typ := surname_typ('Trevisi','Macleod','Marquez');
BEGIN
-- Allowed because they have the same data type
group1 := group2;
-- Not allowed because they have different data types
-- group3 := group2; -- raises an exception
END;
/
Comparing Two Nested Tables:-
DECLARE
TYPE dnames_tab IS TABLE OF VARCHAR2(30);
dept_names1 dnames_tab :=
dnames_tab('Shipping','Sales','Finance','Payroll');
dept_names2 dnames_tab :=
dnames_tab('Sales','Finance','Shipping','Payroll');
dept_names3 dnames_tab :=
dnames_tab('Sales','Finance','Payroll');
BEGIN

52
-- You can use = or !=, but not < or >.
-- These 2 are equal even though members are in different order.
IF dept_names1 = dept_names2 THEN
DBMS_OUTPUT.PUT_LINE(‘dept_names1 and dept_names2 same’):
END IF;
IF dept_names2 != dept_names3 THEN
DBMS_OUTPUT.PUT_LINE(‘dept_names2 and dept_names3 not same’);
END IF;
END;
Using Collection Methods:-

A collection method is a built-in PL/SQL subprogram that returns information about a


collection or operates on a collection. Collection methods make collections easier to
use, and make your applications easier to maintain.You invoke a collection method
using dot notation. You cannot invoke a collection method from a SQL statement.

The only collection method that you can use with an empty collection is EXISTS; all
others raise the exception COLLECTION_IS_NULL.

Collection Methods :-

EXISTS :- returns TRUE if specified element exists in a collection.

COUNT :- returns the total number of elements in a collection.

EXTEND :- increases the size of a collection.

DELETE :- deletes all elements , elements in the specified range, or a particular


element from a collection.

FIRST :- returns subscript of a FIRST element.

LAST :- returns subscript of a LAST element.

PRIOR :- returns subscript that precedes a specified collection subscript

NEXT :- returns subscript that succeed a specified collection subscript .

These methods are accessed with collection name.

EXISTS method :-

EXISTS(n) returns TRUE if the nth element in a collection exists; otherwise, it returns
FALSE. By You can also use EXISTS to avoid referencing a nonexistent element, which
raises an exception. When passed an out-of-range subscript, EXISTS returns FALSE
instead of raising SUBSCRIPT_OUTSIDE_LIMIT.

Example for Checking Whether a Collection Element EXISTS

DECLARE

53
TYPE NumList IS TABLE OF INTEGER;

n NumList := NumList(1,3,5,7);

BEGIN

n.DELETE(2); -- Delete the second element

IF n.EXISTS(1) THEN

DBMS_OUTPUT.PUT_LINE('OK, element #1 exists.');

END IF;

IF n.EXISTS(2) = FALSE THEN

DBMS_OUTPUT.PUT_LINE('OK, element #2 was deleted.');

END IF;

IF n.EXISTS(99) = FALSE THEN

DBMS_OUTPUT.PUT_LINE('OK, element #99 does not exist at all.');

END IF;

END;

Note: You cannot use EXISTS with an associative array.

Example for Counting the Elements in a Collection (COUNT Method):-COUNT


returns the current number of elements in a collection. It is useful when you do not
know how many elements a collection contains. For example, when you fetch a column
of data into a nested table, the number of elements depends on the size of the result
set. For varrays, COUNT always equals LAST. You can increase or decrease the size of
a varray using the EXTEND and TRIM methods, so the value of COUNT can change, up
to the value of the LIMIT method.

For nested tables, COUNT usually equals LAST. However, if you delete elements from
the middle of a nested table, COUNT becomes smaller than LAST. When tallying
elements, COUNT ignores deleted elements. Using DELETE with no parameters sets
COUNT to 0.

Example for Counting Collection Elements with COUNT

DECLARE

TYPE NumList IS TABLE OF NUMBER;

n NumList := NumList(2,4,6,8);

-- Collection starts with 4 elements.

BEGIN

54
DBMS_OUTPUT.PUT_LINE

('There are ' || n.COUNT || ' elements in N.');

n.EXTEND(3); -- Add 3 new elements at the end.

DBMS_OUTPUT.PUT_LINE

('Now there are ' || n.COUNT || ' elements in N.');

n := NumList(86,99); -- Assign a completely new value with 2 elements.

DBMS_OUTPUT.PUT_LINE

('Now there are ' || n.COUNT || ' elements in N.');

n.TRIM(2); -- Remove the last 2 elements, leaving none.

DBMS_OUTPUT.PUT_LINE

('Now there are ' || n.COUNT || ' elements in N.');

END;

Checking the Maximum Size of a Collection (LIMIT Method):-

LIMIT returns the maximum number of elements that a collection can have. If the
collection has no maximum size, LIMIT returns NULL.

Example for Checking the Maximum Size of a Collection with LIMIT

DECLARE

TYPE dnames_var IS VARRAY(7) OF VARCHAR2(30);

dept_names dnames_var :=

dnames_var('Shipping','Sales','Finance','Payroll');

BEGIN

DBMS_OUTPUT.PUT_LINE

('dept_names has ' || dept_names.COUNT || ' elements now');

DBMS_OUTPUT.PUT_LINE

('dept_names''s type can hold a maximum of '

|| dept_names.LIMIT || ' elements');

DBMS_OUTPUT.PUT_LINE

('The maximum number you can use with '

|| 'dept_names.EXTEND() is '

|| (dept_names.LIMIT - dept_names.COUNT));

END;

55
/

Finding the First or Last Collection Element (FIRST and LAST Methods):-

For a collection indexed by integers, FIRST and LAST return the first and last (smallest
and largest) index numbers. For an associative array indexed by strings, FIRST and
LAST return the lowest and highest key values. If the collection is empty, FIRST and
LAST return NULL. If the collection contains only one element, FIRST and LAST return
the same value.

Example f shows how to use FIRST and LAST to iterate through the elements
in a collection that has consecutive subscripts.:-

DECLARE

TYPE NumList IS TABLE OF NUMBER;

n NumList := NumList(1,3,5,7);

counter INTEGER;

BEGIN

DBMS_OUTPUT.PUT_LINE('N''s first subscript is ' || n.FIRST);

DBMS_OUTPUT.PUT_LINE('N''s last subscript is ' || n.LAST);

-- When the subscripts are consecutive starting at 1,

-- it's simple to loop through them.

FOR i IN n.FIRST .. n.LAST

LOOP

DBMS_OUTPUT.PUT_LINE('Element #' || i || ' = ' || n(i));

END LOOP;

n.DELETE(2); -- Delete second element.

-- When the subscripts have gaps or the collection might be uninitialized

-- the loop logic is more extensive.

-- Start at the first element and look for the next element until there are no more.

IF n IS NOT NULL THEN

counter := n.FIRST;

WHILE counter IS NOT NULL

LOOP

DBMS_OUTPUT.PUT_LINE

('Element #' || counter || ' = ' || n(counter));

56
counter := n.NEXT(counter);

END LOOP;

ELSE

DBMS_OUTPUT.PUT_LINE('N is null, nothing to do.');

END IF;

END;

For varrays, FIRST always returns 1 and LAST always equals COUNT.For nested tables,
normally FIRST returns 1 and LAST equals COUNT. But if you delete elements from the
beginning of a nested table, FIRST returns a number larger than 1. If you delete
elements from the middle of a nested table, LAST becomes larger than COUNT.When
scanning elements, FIRST and LAST ignore deleted elements.

Looping Through Collection Elements (PRIOR and NEXT Methods):-

PRIOR(n) returns the index number that precedes index n in a collection. NEXT(n)
returns the index number that succeeds index n. If n has no predecessor, PRIOR(n)
returns NULL. If n has no successor, NEXT(n) returns NULL.For associative arrays with
VARCHAR2 keys, these methods return the appropriate key value; ordering is based on
the binary values of the characters in the string.

These methods are more reliable than looping through a fixed set of subscript values,
because elements might be inserted or deleted from the collection during the loop. This
is especially true for associative arrays, where the subscripts might not be in
consecutive order and so the sequence of subscripts might be (1,2,4,8,16) or
('A','E','I','O','U').

Example Using PRIOR and NEXT to Access Collection Elements :-

DECLARE

TYPE NumList IS TABLE OF NUMBER;

n NumList := NumList(1966,1971,1984,1989,1999);

BEGIN

DBMS_OUTPUT.PUT_LINE('The element after #2 is #' || n.NEXT(2));

DBMS_OUTPUT.PUT_LINE('The element before #2 is #' || n.PRIOR(2));

n.DELETE(3);

-- Delete an element to show how NEXT can handle gaps.

DBMS_OUTPUT.PUT_LINE

57
('Now the element after #2 is #' || n.NEXT(2));

IF n.PRIOR(n.FIRST) IS NULL THEN

DBMS_OUTPUT.PUT_LINE

('Can''t get PRIOR of the first element or NEXT of the last.');

END IF;

END;

You can use PRIOR or NEXT to traverse collections indexed by any series of subscripts.

Example Using NEXT to Access Elements of a Nested Table

DECLARE

TYPE NumList IS TABLE OF NUMBER;

n NumList := NumList(1,3,5,7);

counter INTEGER;

BEGIN

n.DELETE(2); -- Delete second element.

-- When the subscripts have gaps,

-- loop logic is more extensive.

-- Start at first element and look for next element

-- until there are no more.

counter := n.FIRST;

WHILE counter IS NOT NULL

LOOP

DBMS_OUTPUT.PUT_LINE

('Counting up: Element #' || counter || ' = ' || n(counter));

counter := n.NEXT(counter);

END LOOP;

-- Run the same loop in reverse order.

counter := n.LAST;

WHILE counter IS NOT NULL

LOOP

DBMS_OUTPUT.PUT_LINE

58
('Counting down: Element #' || counter || ' = ' || n(counter));

counter := n.PRIOR(counter);

END LOOP;

END;

Increasing the Size of a Collection (EXTEND Method) :-

To increase the size of a nested table or varray, use EXTEND.

This method has three forms:

EXTEND appends one null element to a collection.

EXTEND(n) appends n null elements to a collection.

EXTEND(n,i) appends n copies of the i th element to a collection.

You cannot use EXTEND with index-by tables. You cannot use EXTEND to add elements
to an uninitialized collection. If you impose the NOT NULL constraint on a TABLE or
VARRAY type, you cannot apply the first two forms of EXTEND to collections of that
type.

EXTEND operates on the internal size of a collection, which includes any deleted
elements. This refers to deleted elements after using DELETE(n), but not DELETE
without parameters which completely removes all elements. If EXTEND encounters
deleted elements, it includes them in its tally. PL/SQL keeps placeholders for deleted
elements, so that you can re-create them by assigning new values.

Example Using EXTEND to Increase the Size of a Collection:-

DECLARE

TYPE NumList IS TABLE OF INTEGER;

n NumList := NumList(2,4,6,8);

x NumList := NumList(1,3);

PROCEDURE print_numlist(the_list NumList) IS

output VARCHAR2(128);

BEGIN

FOR i IN the_list.FIRST .. the_list.LAST

LOOP

output :=

59
output || NVL(TO_CHAR(the_list(i)),'NULL') || ' ';

END LOOP;

DBMS_OUTPUT.PUT_LINE(output);

END;

BEGIN

DBMS_OUTPUT.PUT_LINE

('At first, N has ' || n.COUNT || ' elements.');

n.EXTEND(5); -- Add 5 elements at the end.

DBMS_OUTPUT.PUT_LINE

('Now N has ' || n.COUNT || ' elements.');

-- Elements 5, 6, 7, 8, and 9 are all NULL.

print_numlist(n);

DBMS_OUTPUT.PUT_LINE

('At first, X has ' || x.COUNT || ' elements.');

x.EXTEND(4,2); -- Add 4 elements at the end.

DBMS_OUTPUT.PUT_LINE

('Now X has ' || x.COUNT || ' elements.');

-- Elements 3, 4, 5, and 6 are copies of element #2.

print_numlist(x);

END;

When it includes deleted elements, the internal size of a nested table differs from the
values returned by COUNT and LAST. This refers to deleted elements after using
DELETE(n), but not DELETE without parameters which completely removes all
elements. For example, if you initialize a nested table with five elements, then delete
elements 2 and 5, the internal size is 5, COUNT returns 3, and LAST returns 4. All
deleted elements, regardless of position, are treated alike.

Decreasing the Size of a Collection (TRIM Method):-

This method has two forms:

TRIM removes one element from the end of a collection.

TRIM(n) removes n elements from the end of a collection.

If you want to remove all elements, use DELETE without parameters.

60
Note:

You cannot use TRIM with an associative array.

Example Using TRIM to Decrease the Size of a Collection

DECLARE

TYPE NumList IS TABLE OF NUMBER;

n NumList := NumList(1,2,3,5,7,11);

PROCEDURE print_numlist(the_list NumList) IS

output VARCHAR2(128);

BEGIN

IF n.COUNT = 0 THEN

DBMS_OUTPUT.PUT_LINE('No elements in collection.');

ELSE

FOR i IN the_list.FIRST .. the_list.LAST

LOOP

output :=

output || NVL(TO_CHAR(the_list(i)),'NULL') || ' ';

END LOOP;

DBMS_OUTPUT.PUT_LINE(output);

END IF;

END;

BEGIN

print_numlist(n);

n.TRIM(2); -- Remove last 2 elements.

print_numlist(n);

n.TRIM; -- Remove last element.

print_numlist(n);

n.TRIM(n.COUNT); -- Remove all remaining elements.

print_numlist(n);

-- If too many elements are specified,

-- TRIM raises the exception SUBSCRIPT_BEYOND_COUNT.

BEGIN

61
n := NumList(1,2,3);

n.TRIM(100);

EXCEPTION

WHEN SUBSCRIPT_BEYOND_COUNT THEN

DBMS_OUTPUT.PUT_LINE

('There weren''t 100 elements to be trimmed.');

END;

-- When elements are removed by DELETE,

-- placeholders are left behind.

-- TRIM counts these placeholders

-- as it removes elements from the end.

n := NumList(1,2,3,4);

n.DELETE(3); -- delete element 3

-- At this point, n contains elements (1,2,4).

-- TRIMming the last 2 elements

-- removes the 4 and the placeholder, not 4 and 2.

n.TRIM(2);

print_numlist(n);

END;

If n is too large, TRIM(n) raises SUBSCRIPT_BEYOND_COUNT.

Example Using TRIM on Deleted Elements

DECLARE

TYPE CourseList IS TABLE OF VARCHAR2(10);

courses CourseList;

BEGIN

courses := CourseList('Biol 4412', 'Psyc 3112', 'Anth 3001');

courses.DELETE(courses.LAST); -- delete element 3

/* At this point, COUNT equals 2, the number of valid

elements remaining. So, you might expect the next

statement to empty the nested table by trimming

62
elements 1 and 2. Instead, it trims valid element 2

and deleted element 3 because TRIM includes deleted

elements in its tally. */

courses.TRIM(courses.COUNT);

DBMS_OUTPUT.PUT_LINE(courses(1)); -- prints 'Biol 4412'

END;

In general, do not depend on the interaction between TRIM and DELETE. It is better to
treat nested tables like fixed-size arrays and use only DELETE, or to treat them like
stacks and use only TRIM and EXTEND.

Deleting Collection Elements (DELETE Method):-

This method has these forms:

DELETE with no parameters removes all elements from a collection, setting COUNT to
0.

DELETE(n) removes the n th element from an associative array with a numeric key or a
nested table. If the associative array has a string key, the element corresponding to
the key value is deleted. If n is null, DELETE(n) does nothing.

DELETE(m,n) removes all elements in the range m..n from an associative array or
nested table. If m is larger than n or if m or n is NULL, DELETE(m,n) does nothing.

Example Using the DELETE Method on a Collection:-

DECLARE

TYPE NumList IS TABLE OF NUMBER;

n NumList := NumList(10,20,30,40,50,60,70,80,90,100);

TYPE NickList IS TABLE OF VARCHAR2(64) INDEX BY VARCHAR2(32);

nicknames NickList;

BEGIN

n.DELETE(2); -- deletes element 2

n.DELETE(3,6); -- deletes elements 3 through 6

n.DELETE(7,7); -- deletes element 7

n.DELETE(6,3); -- does nothing since 6 > 3

n.DELETE; -- deletes all elements

nicknames('Bob') := 'Robert';

63
nicknames('Buffy') := 'Esmerelda';

nicknames('Chip') := 'Charles';

nicknames('Dan') := 'Daniel';

nicknames('Fluffy') := 'Ernestina';

nicknames('Rob') := 'Robert';

-- following deletes element denoted by this key

nicknames.DELETE('Chip');

-- following deletes elements with keys in this alphabetic range

nicknames.DELETE('Buffy','Fluffy');

END;

Varrays always have consecutive subscripts, so you cannot delete individual elements
except from the end by using the TRIM method. You can use DELETE without
parameters to delete all elements.

If an element to be deleted does not exist, DELETE(n) simply skips it; no exception is
raised. PL/SQL keeps placeholders for deleted elements, so you can replace a deleted
element by assigning it a new value. This refers to deleted elements after using
DELETE(n), but not DELETE without parameters which completely removes all
elements.

Bulk Collect Operation:-

Normally a developer will use a cursor to retrieve and process multiple rows of data,
one at a time, but there are performance problems when dealing with large numbers of
rows using cursors. As we have seen, a cursor fetches one row at a time, holding a
consistent view, until all rows have been retrieved or the cursor is closed.

A performance issue arises from the fact that there are two engines in the database,
the PL/SQL engine and the SQL engine. When a cursor fetches a row of data it
performs a “context switch” to the SQL engine, and it is the SQL component that
retrieves the data. The SQL engine places the data in-memory and another context
switch places us back into the PL/SQL engine.

The PL/SQL engine then continues processing until the next row is required, and the
process repeats. A context switch is very fast, but if performed over and over again,
the switching can take a noticeable amount of time. A bulk collect is a method of
fetching data where the PL/SQL engine tells the SQL engine to collect many rows at
once and place them in a collection. The SQL engine retrieves all the rows and loads

64
them into the collection and switches back to the PL/SQL engine. All the rows are
retrieved with only 2 context switches. The larger the number of rows processed, the
more performance is gained by using a bulk collect.

In the Oracle10g database, the PL/SQL engine may perform a bulk collect for you. In
10g, a cursor loop may cause the PL/SQL engine to automatically bulk collect 100 rows
at a time, allowing your code to process rows without having to setup and execute the
bulk collect operation. As a result of this performance enhancement in 10g, bulk
collecting 75 rows may not provide you with much of a benefit, while bulk collecting
large numbers of rows (many hundreds) will still provide you with increased
performance.

Example:-

DECLARE
TYPE etype IS TABLE OF varchar2(20) INDEX BY BINARY_INTEGER;
e etype;
BEGIN
SELECT ENAME BULK COLLECT INTO e FROM EMP;
FOR x IN e.FIRST.e.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(e(x));
END LOOP;
END;
Note :- prior to oracle 9i R2 we can bulk collect only single column values, from 9i R2
we can also bulk collect records .

BulkCollecting Records :-

DECLARE
TYPE etype IS TABLE OF EMP%ROWTYPE INDEX BY BINARY_INTEGER;
E etype;
x number;
BEGIN
SELECT * BULK COLLECT INTO E FROM EMP;
x := E.FIRST;
WHILE(x <= E.LAST)
LOOP
DBMS_OUTPUT.PUT_LINE(E(X).EMPNO||’ ‘||E(X).ENAME||’ ‘||E(X).SAL);
x := E.NEXT(x);
END LOOP;
END;

65
A PL/SQL program to display all employee records (backward navigation):-

DECLARE
TYPE etype IS TABLE OF EMP%ROWTYPE INDEX BY BINARY_INTEGER;
e etype;
x number;
BEGIN
SELECT * BULK COLLECT INTO e FROM EMP;
x := e.LAST;
WHILE(x >= e.FIRST)
LOOP
DBMS_OUTPUT.PUT_LINE(e(x).EMPNO||’ ‘||e(x).ENAME||’ ‘||e(x).SAL);
x := e.PRIOR(x);
END LOOP;
END;
BULK BIND using FORALL loop :-
The FORALL syntax allows us to bind the contents of a collection to a single DML
statement, allowing the DML to be run for each row in the collection without requiring a
context switch each time. FOR ALL loop improves performance of BULK INSERT,BULK
UPDATE,BULK DELETE.
FORALL loop allows only statement and that must be DML statement.

Syntax :-
FORALL <var> in <low>..<upp>
DML statement
Example for BULK INSERT:-
EMP_TEMP
EMPNO ENAME SAL
Using FORALL and BULK COLLECT copy data from EMP TO EMP_TEMP
DECLARE
type empno_array is table of emp.empno%type
index by binary_integer;
type ename_array is table of emp.ename%type
index by binary_integer;
type sal_array is table of emp.sal%type
index by binary_integer;
e empno_array;
n ename_array;
s sal_array;
t1 number;

66
t2 number;
BEGIN
select empno,ename,sal bulk collect into e,n,s from emp;
t1 := dbms_utility.get_time;
/* using FOR loop */
FOR i in e.first..e.last
loop
insert into emp_temp values(e(i),n(i),s(i));
end loop;
t2 := dbms_utility.get_time;
dbms_output.put_line('using FOR loop :-'||to_char(t2-t1));
execute immediate 'truncate table emp_temp';
t2 := dbms_utility.get_time;
/* using FORALL loop */
FORALL i in e.first..e.last
insert into emp_temp values(e(i),n(i),s(i));
t2 := dbms_utility.get_time;
dbms_output.put_line('using FORALL loop :-'||to_char(t2-t1));
END;
/

Example BULK UPDATE :-


Using FORALL loop increment employee salaries by 1000
DECLARE
TYPE emp_list is table of emp.empno%TYPE
index by binary_integer;
E emp_list;
BEGIN
SELECT EMPNO BULK COLLECT INTO E FROM EMP;
FORALL i in E.FIRST..E.LAST
UPDATE EMP SET SAL=SAL+1000 WHERE EMPNO=E(i);
END;
/
Example BULK DELETE :-
Using FORALL loop Delete All Employee Rows
DECLARE
TYPE emp_list is table of emp.empno%TYPE
index by binary_integer;
E emp_list;

67
BEGIN
SELECT EMPNO BULK COLLECT INTO E FROM EMP;
FORALL I in E.FIRST..E.LAST
DELETE FROM EMP WHERE EMPNO=E(I);
END;
SQL%BULK_ROWCOUNT:-
The SQL%BULK_ROWCOUNT cursor attribute gives information about the rows
affected by each iteration of the FORALL statement.
Example :-
DECLARE
TYPE etype IS TABLE OF number(2);
e etype := etype(10,20,30,40);
BEGIN
/* PERFORM BULK DELETE OPERATION */
FORALL i IN e.FIRST..e.LAST
DELETE FROM emp WHERE deptno = e(i);
/* ROWS AFFECTED */
FOR i IN e.FIRST..e.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(‘ Deptno = ‘||e(i)||’ ‘||
’Rows affected = ‘||SQL%BULK_ROWCOUNT(i));
END LOOP;
END;
OUTPUT :-
Deptno =10 Rows affected = 3
Deptno = 20 Rows affected = 5
Deptno = 30 Rows affected = 6
Deptno = 40 Rows affected = 0
SAVE EXCEPTIONS and SQL%BULK_EXCEPTION :-
We saw how the FORALL syntax allows us to perform bulk DML operations, but what
happens if one of those individual operations results in an exception? If there is no
exception handler, all the work done by the current bulk operation is rolled back. If
there is an exception handler, the work done prior to the exception is kept, but no
more processing is done. Neither of these situations is very satisfactory, so instead we
should use the SAVE EXCEPTIONS clause to capture the exceptions and allow us to
continue past them. We can subsequently look at the exceptions by referencing the
SQL%BULK_EXCEPTION cursor attribute.
To see this in action create the following table :-
SQL>CREATE TABLE exception_test ( id NUMBER(10) NOT NULL);
The following code creates a collection with 100 rows, but sets the value of rows 50
and 51 to NULL. Since the above table does not allow nulls, these rows will result in an

68
exception. The SAVE EXCEPTIONS clause allows the bulk operation to continue past
any exceptions, but if any exceptions were raised in the whole operation, it will jump to
the exception handler once the operation is complete. In this case, the exception
handler just loops through the SQL%BULK_EXCEPTION cursor attribute to see what
errors occured.
DECLARE
TYPE t_tab IS TABLE OF exception_test.ID%TYPE;
l_tab t_tab := t_tab();
l_error_count NUMBER;
BEGIN
-- Fill the collection.
FOR i IN 1 .. 100
LOOP
l_tab.extend;
l_tab(l_tab.last) := i;
END LOOP;
-- cause failure
l_tab(50) := NULL;
l_tab(51) := NULL;
-- perform bulk insert
FORALL i IN l_tab.first .. l_tab.last SAVE EXCEPTIONS
INSERT INTO exception_test VALUES (l_tab(i));
EXCEPTION
WHEN others THEN
l_error_count := SQL%BULK_EXCEPTIONS.count;
DBMS_OUTPUT.put_line('Number of failures: ' || l_error_count);
FOR i IN 1 .. l_error_count LOOP
DBMS_OUTPUT.put_line('Error: ' || i ||
' Array Index: ' || SQL%BULK_EXCEPTIONS(i).error_index ||
' Message: ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
END LOOP;
END;
PL/SQL RECORDS :-

A PL/SQL record is a collection of elements of different datatypes.

ORACLE supports 3 types of PL/SQL records

 Table records
 Cursor records
 User defined records
Table Records :-

69
A table record is declared as follows

E EMP%ROWTYPE;

SELECT * INTO E FROM EMP WHERE EMPNO=7844;

Cursor Recorrds :-

A cursor is declared as follows

CURSOR C1 IS SELECT ENAME,SAL FROM EMP;

R C1%ROWTYPE;

User Defined Records :-

To use user defined record

 Declare user defined record type


 Declare variable of user defined record type
Declaring User Defined Record type :-

Syntax:-

TYPE <name> IS RECORD


(COL1 DATATYPE(SIZE),
COL2 DATATYPE(SIZE),
--------------------);
Example:-
TYPE rectype IS RECORD
(ENO NUMBER(4),
ENAME VARCHAR2(20),
SAL NUMBER(7,2),
DNAME VARCHAR2(20),
LOC VARCHAR2(20),
GRADE NUMBER(2)
);
Declaring Variable :-

R rectype;

Example:-

DECLARE
TYPE rectype IS RECORD
(ENO NUMBER(4),
ENAME VARCHAR2(20),
SAL NUMBER(7,2),
DNAME VARCHAR2(20),
LOC VARCHAR2(20),

70
GRADE NUMBER(2)
);
TYPE arrtype IS TABLE OF rectype INDEX BY BINARY_INTEGER;
R rectype;
BEGIN
SELECT E.EMPNO,E.ENAME,E.SAL,
D.DNAME,D.LOC,
S.GRADE BULK COLLECT
INTO R
FROM EMP E, DEPT D, SALGRADE S
WHERE E.DEPTNO = D.DEPTNO AND E.SAL BETWEEN S.LOSAL AND S.HISAL;
FOR I IN R.FIRST..R.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(R(I).EMPNO||’ ‘||R(I).ENAME||’ ‘||R(I).DNAME||’ ‘||
R(I).LOC||’ ‘||R(I).GRADE);
END LOOP;
END;
Assigning Values to Tables with Complex Data Types

DECLARE
TYPE emp_name_rec is RECORD (
ename emp.ename%TYPE,
sal emp.sal%TYPE,
hiredate emp.hiredate%TYPE
);
-- Table type that can hold information about employees
TYPE EmpList_tab IS TABLE OF emp_name_rec;
SeniorSalespeople EmpList_tab;
-- Declare a cursor to select a subset of columns.
CURSOR c1 IS SELECT ename,sal,hiredate
FROM emp;
EndCounter NUMBER := 10;
TYPE EmpCurTyp IS REF CURSOR;
emp_cv EmpCurTyp;
BEGIN
OPEN emp_cv FOR SELECT ename,sal,hiredate
FROM emp
WHERE job = 'SALESMAN' ORDER BY hiredate;
FETCH emp_cv BULK COLLECT INTO SeniorSalespeople;
CLOSE emp_cv;

71
-- for this example, display a maximum of ten employees
IF SeniorSalespeople.LAST > 0 THEN
IF SeniorSalespeople.LAST < 10 THEN
EndCounter := SeniorSalespeople.LAST;
END IF;
FOR i in 1..EndCounter LOOP
DBMS_OUTPUT.PUT_LINE
(SeniorSalespeople(i).ename|| ', '
|| SeniorSalespeople(i).sal|| ', ' || SeniorSalespeople(i).hiredate);
END LOOP;
END IF;
END;
/
Exception Handling in PL/SQL:-

Runtime errors are called Exceptions. If at any time an error occurs in the PL/SQL block
at that point pl/sql block execution is stopped and oracle returns error message. To
continue the program execution and to display user friendly message exception needs
to be handled , to handle exception include EXCEPTION block in PL/SQL program as
follows

a PL/SQL block has the following structure:

DECLARE

<VARIABLES> ;

BEGIN

EXECUTABLE STATEMENTS;

EXCEPTION

WHEN EXCEPTION_NAME THEN

ERROR – PROCESSING STATEMENTS;

END;

Handling Raised PL/SQL Exceptions :-

When an exception is raised, normal execution of your PL/SQL block or subprogram


stops and control transfers to its exception-handling part, which is formatted as
follows:
EXCEPTION
WHEN exception1 THEN -- handler for exception1
sequence_of_statements1

72
WHEN exception2 THEN -- another handler for exception2
sequence_of_statements2
WHEN OTHERS THEN -- optional handler for all other errors
sequence_of_statements3
END;

To catch raised exceptions, you write exception handlers. Each handler consists of a
WHEN clause, which specifies an exception, followed by a sequence of statements to be
executed when that exception is raised. These statements complete execution of the
block or subprogram; control does not return to where the exception was raised. In
other words, you cannot resume processing where you left off. The optional OTHERS
exception handler, which is always the last handler in a block or subprogram, acts as
the handler for all exceptions not named specifically. Thus, a block or subprogram can
have only one OTHERS handler. Use of the OTHERS handler guarantees that no
exception will go unhandled.If you want two or more exceptions to execute the same
sequence of statements, list the exception names in the WHEN clause, separating them
by the keyword OR, as follows:

EXCEPTION

WHEN over_limit OR under_limit OR VALUE_ERROR THEN

-- handle the error

If any of the exceptions in the list is raised, the associated sequence of statements is
executed. The keyword OTHERS cannot appear in the list of exception names; it must
appear by itself. You can have any number of exception handlers, and each handler
can associate a list of exceptions with a sequence of statements. However, an
exception name can appear only once in the exception-handling part of a PL/SQL block
or subprogram.In PL/SQL exceptions are two types

1 system defined Exceptions

2 user defined Exceptions

1 system defined Exception :-

73
74
NO_DATA_FOUND example :-

DECLARE
V_ENAME VARCHAR2(20);
V_SAL NUMBER(10);
BEGIN
SELECT ENAME ,SAL INTO V_ENAME,V_SAL FROM EMP
WHERE EMPNO=&NO;
DBMS_OUTPUT.PUT_LINE(V_ENAME||' '||V_SAL);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('no such employee’);
END;
/

executing the program

Enter value for no: 7902


FORD 3000
/
Enter value for no: 8888
No such employee

TOO_MANY_ROWS example :-

DECLARE
V_SAL NUMBER(10);
BEGIN
SELECT SAL INTO V_SAL FROM EMP;
DBMS_OUTPUT.PUT_LINE(V_SAL);
EXCEPTION
WHEN TOO_MANY_ROWS THEN

75
DBMS_OUTPUT.PUT_LINE('Fetching More than One row’);
END;

OUTPUT:-
Fetching More than One row
Example:-

DECLARE
VEMPNO NUMBER(5);
VNAME VARCHAR2(20);
VDEPTNO NUMBER(2);
BEGIN
SELECT EMPNO,ENAME,DEPTNO INTO VEMPNO,VNAME,VDEPTNO FROM EMP
WHERE EMPNO=7788;
DBMS_OUTPUT.PUT_LINE('THE SCOTT WORKS IN DEPTNO:'||VDEPTNO);
SELECT EMPNO,ENAME,DEPTNO INTO VEMPNO,VNAME,VDEPTNO FROM EMP
WHERE DEPTNO=10;
DBMS_OUTPUT.PUT_LINE(VNAME);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No such Employee’);
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('Fetching More than One row’);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(‘Unknown Error’);
END;
/

OUTPUT:-
THE SCOTT WORKS IN DEPTNO:20
ERROR:MORE THAN ONE EMPLOYEE WORKS IN DEPNTO 10

Zero_divide Example :-

DECLARE
A NUMBER(10);
B NUMBER(10);
C NUMBER(10);
BEGIN
A:=&a;
B:=&b;
C:=A/B;
DBMS_OUTPUT.PUT_LINE(C);
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('divide by zero error');
END;
/

Input :-
A := 10
B := 0

Output :-

76
Divide by zero error
Invalid_cursor Example :-

DECLARE
CURSOR C1 IS SELECT *FROM EMP WHERE SAL>2000;
I EMP%ROWTYPE;
BEGIN
LOOP
FETCH C1 INTO I;
EXIT WHEN C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(I.ENAME||' '||I.SAL);
END LOOP;
CLOSE C1;
EXCEPTION
WHEN INVALID_CURSOR THEN
DBMS_OUTPUT.PUT_LINE('First Open the Cursor');
END;
/
OUTPUT:

First Open the Cursor

Cursor already open Example :-

DECLARE
CURSOR C1 IS SELECT *FROM EMP WHERE SAL>2000;
I EMP%ROWTYPE;
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO I;
EXIT WHEN C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(I.ENAME||' '||I.SAL);
END LOOP;
OPEN C1;
EXCEPTION
WHEN CURSOR_ALREADY_OPEN THEN
DBMS_OUTPUT.PUT_LINE('WE MUST CLOSE THE CURSOR BEFOR REOPENING');
END;
/

invalid_number :-

When we are try to convert string type to number type oracle server returns
INVALID_NUMBER exception.

Example:-

BEGIN
INSERT INTO EMP(EMPNO,SAL)VALUES(1,'ABC');
EXCEPTION
WHEN INVALID_NUMBER THEN
DBMS_OUTPUT.PUT_LINE('INSERTING APPROPRIATE DATA');
END;

77
/

value_error Example :-

DECLARE
Z NUMBER(10);
BEGIN
Z:='&X'+'&Y';
DBMS_OUTPUT.PUT_LINE(Z);
EXCEPTION
WHEN VALUE_ERROR THEN
DBMS_OUTPUT.PUT_LINE('ENTER PROPER DATA');
END;

value error also occurs when we try to store more data than the specified data type
size in a variable
Example :-

DECLARE
Z VARCHAR2(3);
BEGIN
Z:='ABCD';
EXCEPTION
WHEN VALUE_ERROR THEN
DBMS_OUTPUT.PUT_LINE('INVALID STRING LENGTH');
END;
OUTPUT:

INVALID STRING LENGTH


Dup_val_on_index:- Raised when duplicate value entered into primary key column.

Example:-

DECLARE
V_EMPNO EMP.EMPNO%TYPE:=&EMPNO;
V_ENAME EMP.ENAME%TYPE:=&ENAME;
V_DEPTNO EMP.DEPTNO%TYPE:=&DEPTNO;
BEGIN
INSERT INTO EMP(EMPNO,ENAME,DEPTNO)
VALUES(V_EMPNO,V_ENAME,V_DEPTNO);
COMMIT;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
DBMS_OUTPUT.PUT_LINE(‘Employee Already Exists’);
END;

SQLCODE & SQLERRM :-

PL/SQL provides following built-in functions which are used in error handling.

 SQLCODE
 SQLERRM
SQLCODE returns ERROR CODE

78
SQLERRM returns ERROR MESSAGE

Example :-

DECLARE
X NUMBER;
Y NUMBER;
Z NUMBER;
BEGIN
X := &X;
Y := &Y;
Z := X/Y;
DBMS_OUTPUT.PUT_LINE(Z);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE);
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
Programmer Defined Exceptions :-

These EXCEPTIONS are defined by user and must be raised by user by using

1 RAISE statement

2 RAISE_APPLICATION_ERROR built-in procedure.

Using RAISE statement :-

Syntax :-

RAISE <exception-name>;

Exception must be declared in declaration part.

Example :-

DECLARE
X NUMBER(2);
Y NUMBER(2);
Z NUMBER(2);
ONE_DIVIDE EXCEPTION;
BEGIN
X := &X;
Y := &Y;
IF Y=1 THEN
RAISE ONE_DIVIDE;
END IF;

79
Z := X/Y;
DBMS_OUTPUT.PUT_LINE(Z);
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE(‘ZERO DIVIDE ERROR’);
WHEN ONE_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE(‘ONE DIVIDE ERROR’);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
Using RAISE_APPLICATION_ERROR :-

This is a built-in procedure. It is used to display user defined exception messages in


more descriptive form. If we want to display user defined exception messages as oracle
predefined errors format then use this procedure.

Syntax:-

RAISE_APPLICATION_ERROR(ERROR NUMBER,ERROR MSG);

ERROR NUMBER can be between -20000 and -20999.

Example :-

DECLARE
X NUMBER(2);
Y NUMBER(2);
Z NUMBER(2);
BEGIN
X := &X;
Y := &Y;
IF Y=1 THEN
RAISE_APPLICATION_ERROR(-20001,’ONE DIVIDE ERROR’);
END IF;
Z := X/Y;
DBMS_OUTPUT.PUT_LINE(Z);
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE(‘ZERO DIVIDE ERROR’);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
PRAGMA EXCEPTION_INIT :-

80
It is the way to associate user defined exception with oracle Predefined error.If the
oracle predefined error is not having name then we can assign name to that error with
the help of PRAGMA EXCEPTION_INIT.

Syntax :-

PRAGMA EXCEPTION_INIT(user defined exception,oracle error);

Example :-

DECLARE

VDNO DEPT.DEPTNO%TYPE;

child_exists EXCEPTION;

PRAGMA EXCEPTION_INIT(child_exists,-2091);

BEGIN

VDNO := &DEPTNO;

DELETE FROM DEPT WHERE DEPTNO=VDNO;

DBMS_OUTPUT.PUT_LINE(‘RECORD DELETED SUCCESSFULLY’);

EXCEPTION

WHEN child_exists THEN

DBMS_OUTPUT.PUT_LINE(‘child records present’);

END;

Input :- Deptno :- 10

If at least one employees work for 10 th dept then DELETE statement causes
child_exists exception.

Example :- create a table with following structure and constraints

CREATE TABLE emp(Empno number(4) primary key,

Ename varchar2(20) ,

Sal number(7,2) check(sal > 3000) ;

Write a pl/sql prog to insert record into the above table ?

DECLARE

VENO EMP.EMPNO%TYPE;

VENAME EMP.ENAME%TYPE;

VSAL EMP.SAL%TYPE;

CHECK_VIOLATED EXCEPTION;

PRAGMA EXCEPTION_INIT(CHECK_VIOLATED,-2290);

81
BEGIN

VENO := &ENO;

VENAME := '&ENAME';

VSAL := &SAL;

INSERT INTO EMP55 VALUES(VENO,VENAME,VSAL);

COMMIT;

EXCEPTION

WHEN DUP_VAL_ON_INDEX THEN

DBMS_OUTPUT.PUT_LINE('DUPLICATE KEY FOUND');

WHEN CHECK_VIOLATED THEN

DBMS_OUTPUT.PUT_LINE('CHECK CONSTRAINT VIOLATED');

END;

After executing the above program if user enters sal less than 3000 sal then insert
statement causes check_violated exception.

How PL/SQL Exception Propogates :-When an exception is raised, if PL/SQL cannot


find a handler for it in the current block or subprogram, the exception propagates. That
is, the exception reproduces itself in successive enclosing blocks until a handler is
found or there are no more blocks to search. If no handler is found, PL/SQL returns
an unhandled exception error to the host environment. Exceptions cannot propagate
across remote subprogram calls done through database links. A PL/SQL block cannot
catch an exception raised by a remote subprogram.

Propogation Rules Example 1:-

82
Propogation Rules Example 2 :-

Propogation Rules Example 3 :-

83
Example :-

BEGIN

DECLARE

X NUMBER(4) := &X;

BEGIN

DBMS_OUTPUT.PUT_LINE(X);

EXCEPTION

WHEN VALUE_ERROR THEN

DBMS_OUTPUT.PUT_LINE('SIZE MISMATCH');

END;

EXCEPTION

WHEN VALUE_ERROR THEN

DBMS_OUTPUT.PUT_LINE('VALUE ERROR');

END;

In the above example if entered value for X is 10000 then the statement causes
VALUE_ERROR exception but that cannot be handled by inner exeception block so it is
propagated to outer block, outer block will handle that exception prints message
VALUE ERROR.

Frequently asked questions:-

What is Exception in Oracle?

84
Errors occur during runtime processing due to either hardware or network failure or
application logic errors are known as exception.

Explain the types of Exceptions. ?

Predefined oracle exceptions

User-defined exceptions

What are the predefined oracle exceptions ?

Following is the predefined oracle exceptions

No_data_found Too_many_rows Zero_divide Login_denied Program_error

Timeout_on_resource Invalid_cursor Cursor_already_open Dup_val_on_index

Explain user defined exceptions in oracle. ?

User defined exception in oracle are defined by the user. They need to be explicitly
raised by the user using the RAISE statement. Oracle is not aware of these exceptions

How Userdefijed Exceptions Are Raised ?

Using RAISE statement

Using RAISE_APPLICATION_ERROR procedure

Explain the guidelines for Avoiding and Handling PL/SQL Errors and
Exceptions.

Guidelines for Avoiding and Handling PL/SQL Errors and Exceptions

• Use both error checking and exception handling to ensure your program can
handle all possibilities.

• Add exception handlers whenever there is any possibility of an error occurring.

• Add error-checking code whenever you can predict that an error might occur if
your code gets bad input data.

• Make your programs robust enough to work even if the database is not in the
state you expect.

• Handle named exceptions whenever possible, instead of using WHEN OTHERS in


exception handlers.

• Test your code with different combinations of bad data to see what potential
errors arise.

• Write out debugging information in your exception handlers.

• Carefully consider whether each exception handler should commit the


transaction, roll it back, or let it continue

What is Pragma Init Exception in oracle?

85
Pragma Init Exception is declared using the following syntax:-

PRAGMA EXCEPTION_INIT(exception_name, oracle_error_nb);

This directs the complier to add the user defined error to the oracle error number. It
associate the user defined error to predefined error codes.

What is Raise_application_error?

Raise_application_error is a procedure of package DBMS_STANDARD which


allows to issue an user_defined error messages from stored sub-program or database
trigger.

What are the return values of functions SQLCODE and SQLERRM?

SQLCODE returns the latest code of the error that has occured.

SQLERRM returns the relevant error message of the SQLCODE.

Can we define exceptions twice in same block ?

No.

What are PL/SQL Cursor Exceptions?

Cursor_Already_Open, Invalid_Cursor

Can the PL/SQL block process more than one exception at a time?

No, the PL/SQL block cannot handle more than one exception. It can process only one
exception handler before terminating.

Is it possible to have more than one OTHERS clause?

No, it is not possible to have more than one OTHERS clause in the exception section.

What causes the INVALID_CURSOR exception?

This exception is raised when the program attempts to perform an illegal operation,
such as closing an unopened cursor.

Where will the control be transferred if an exception is raised within an


exception section of the block?

When an exception is raised within an exception section of a block, the control is


transferred to the exception section of the enclosing block.

What is the advantage of OTHERS exception handler?

The OTHERS exception handler ensures that no exception goes unhanded and the
program terminates successfully.

When is the CURSOR_ALREADY_OPEN exception raised?

This exception is raised when the program tries to open an already opened cursor.
The cursor should be closed before it can be opened again.

86
What is the advantage of having a separate exception section within a
PL/SQL code?

The advantages of having a separate exception section within a PL/SQL code are as
follows:

1) It is easier to understand the program logic as it is easily visible and readable

2) Any error in any statement will be detected and handled easily.

What happen when there is no handler for a raised exception?

If an exception is raised and there is no handler in the current block, the exception
would propagate to the enclosing block. If no handler is found in the enclosing block,
the exception will be propagated to the calling environment as an unhandled exception.

What is the difference between user-defined exceptions and Oracle-defined


exceptions?

User-defined exceptions are explicitly raised; whereas, Oracle-defined exceptions are


raised implicitly.

What is the difference between RAISE and RAISE.APPLICATIONJERROR?

RAISE is used to call pre-defined exceptions declared in the declarative section of the
PL/SQL block.

RAISE_APPLICATION.„ERROR is used to call user-defined ORA – error messages from


stored subprograms.

Can processing be resumed from the point exception was raised after the
exception is handled?

After the exception is handled, processing cannot be resumed within the executable
section of the current block from where the exception was raised. The current block
where the exception handier is declared will be terminated. The control will pass to the
statement in the enclosing executable block. If there is no enclosing block, control will
pass back to the calling host environment.

PL/SQL subprograms :-

A PL/SQL subprogram is a named PL/SQL block that can be invoked with a set of
parameters. A subprogram can be either a procedure or a function. Typically, you use a
procedure to perform an action and a function to compute and return a value.

You can create a subprogram either at schema level, inside a package, or inside a


PL/SQL block (which can be another subprogram).

87
A subprogram created at schema level is a standalone stored subprogram. You
create it with the CREATE PROCEDURE or CREATE FUNCTION statement. It is
stored in the database until you drop.

A subprogram created inside a package is a packaged subprogram. It is stored in


the database until you drop the package with the DROP PACKAGE statement.

A subprogram created inside a PL/SQL block is a nested subprogram. A nested


subprogram is stored in the database only if it is nested within a standalone or
packaged subprogram.

Reasons to use Subprogram :-

 Allows writing PL/SQL programs to meet the requirements

 Allows breaking the PL/SQL program into manageable modules

 Allows reusing of code spec and thus improves code maintainability

 Enforces security by giving appropriate permissions, on a procedure or


function

 Improves database performance as no compilation is required to execute


the stored subprograms.

 Subprograms can be grouped into PL/SQL packages.

Packages make code even more reusable and maintainable, and can be


used to define an API.

 You can hide the implementation details of subprograms by placing them


in PL/SQL packages.

You can define subprograms in a package body without declaring their


specifications in the package specification. However, such subprograms
can be invoked only from inside the package. At least one statement must
appear in the executable part of a subprogram. The NULL statement
meets this requirement.
Parts of the Subprograms :-

A subprogram always has a name, and can have a parameter list.

Like every PL/SQL block, a subprogram has an optional declarative part, a required
executable part, and an optional exception-handling part

The declarative part of a subprogram does not begin with the keyword DECLARE, as


the declarative part of a non-subprogram block does. The declarative part contains
declarations of types, cursors, constants, variables, exceptions, and nested
subprograms. These items are local to the subprogram and does not exists when the
subprogram completes execution.

The executable part of a subprogram contains statements that assign values, control


execution, and manipulate data.

88
The exception-handling part of a subprogram contains code that handles run-time
errors.

Example :-

declares and defines a function (at the same time) inside an anonymous block. The
function has the optional declarative part and the required executable part, but not the
optional exception-handling part. The executable part of the anonymous block invokes
the function.

DECLARE

-- Function declaration and definition begins

FUNCTION square (original NUMBER)

RETURN NUMBER -- RETURN clause

AS

-- Declarative part of function begins

original_squared NUMBER;

-- Declarative part of function ends

-- Executable part of function begins

BEGIN

original_squared := original * original;

RETURN original_squared; -- RETURN statement

-- Exception-handling part of function (optional) goes here

END;

-- Executable part of function ends

-- Function declaration and definition ends

-- Declarative part of block ends

-- Executable part of block begins

BEGIN

DBMS_OUTPUT.PUT_LINE(square(100)); -- Function invocation

END;

-- Executable part of block ends

Creating Nested Subprograms that Invoke Each Other :-

In a block, you can create multiple nested subprograms. If they invoke each other, you
need forward declaration, because a subprogram must be declared before it can be
invoked. With forward declaration, you declare a subprogram, but do not define it until

89
after you have defined the other subprograms that invoke it. A forward declaration and
its corresponding definition must appear in the same block.

creates two procedures that invoke each other:-

DECLARE

-- Declare proc1 (forward declaration):

PROCEDURE proc1(number1 NUMBER);

-- Declare and define proc 2:

PROCEDURE proc2(number2 NUMBER) IS

BEGIN

proc1(number2);

END;

-- Define proc 1:

PROCEDURE proc1(number1 NUMBER) IS

BEGIN

proc2 (number1);

END;

BEGIN

NULL;

END;

Declaring and Passing Subprogram Parameters:-

A subprogram heading can declare formal parameters. Each formal parameter


declaration can specify a mode and a default value. When you invoke the subprogram,
you can pass actual parameters to it.

Formal and Actual Subprogram Parameters:-

Formal parameters are the variables declared in the subprogram header and
referenced in its execution part. Actual parameters are the variables or expressions
that you pass to the subprogram when you invoke it. Corresponding formal and actual
parameters must have compatible data types.A good programming practice is to use
different names for formal and actual parameters

DECLARE
emp_num NUMBER(6) := 120;
bonus NUMBER(6) := 100;

90
merit NUMBER(4) := 50;
PROCEDURE raise_salary (
emp_id NUMBER, -- formal parameter
amount NUMBER -- formal parameter
) IS
BEGIN
UPDATE employees
SET salary = salary + amount
WHERE employee_id = emp_id;
END raise_salary;
BEGIN
raise_salary(emp_num, bonus); -- actual parameters
raise_salary(emp_num, merit + bonus); -- actual parameters
END;
/

When you invoke a subprogram, PL/SQL evaluates each actual parameter and assigns
its value to the corresponding formal parameter. If necessary, PL/SQL implicitly
converts the data type of the actual parameter to the data type of the corresponding
formal parameter before the assignment (this is why corresponding formal and actual
parameters must have compatible data types).

A good programming practice is to avoid implicit conversion, either by using explicit


conversion (explained in Explicit Conversion) or by declaring the variables that you
intend to use as actual parameters with the same data types as their corresponding
formal parameters

Specifying Subprogram Parameter Modes:-


Parameter modes define the action of formal parameters. The three parameter modes
are IN (the default), OUT, and IN OUT.
Any parameter mode can be used with any subprogram. Avoid using the OUT and IN
OUT modes with functions. To have a function return multiple values is poor
programming practice. Also, make functions free from side effects, which change the
values of variables not local to the subprogram.
Parameters are 3 types

 IN
 OUT
 IN OUT
IN parameter :-

 IN parameter always receives value from main program.

 The value of an IN parameter is a constant.

 It cannot be changed or reassigned within the module.

 IN parameter can be declared with default value.

 It is default.

91
OUT parameter :-

 OUT parameter always sends value to main program.

 An OUT parameter is a variable and not a constant

 It can be changed or reassigned within the module

 A default value cannot be assigned to an OUT parameter.

IN OUT parameter :-

 It functions as an IN or an OUT parameter or both.

 It receives values and can also send value to main program.

 An IN/OUT parameter is a variable and not a constant.

 A default value cannot be assigned to an OUT parameter.

Example Using IN,OUT parameters :-

DECLARE

emp_num NUMBER(6) := 120;

bonus NUMBER(6) := 50;

emp_last_name VARCHAR2(25);

PROCEDURE raise_salary (emp_id IN NUMBER, amount IN NUMBER,

emp_name OUT VARCHAR2) IS

BEGIN

UPDATE employees SET salary =

salary + amount WHERE employee_id = emp_id;

SELECT last_name INTO emp_name

FROM employees

WHERE employee_id = emp_id;

END raise_salary;

BEGIN

raise_salary(emp_num, bonus, emp_last_name);

DBMS_OUTPUT.PUT_LINE

('Salary was updated for: ' || emp_last_name);

END;

Note :-

92
Before exiting a subprogram, assign values to all OUT formal parameters. Otherwise,
the corresponding actual parameters will be null. If you exit successfully, PL/SQL
assigns values to the actual parameters. If you exit with an unhandled exception,
PL/SQL does not assign values to the actual parameters.

Summary of Parameter Modes :-

Overloading PL/SQL Subprogram Names:-

PL/SQL lets you overload local subprograms, packaged subprograms, and type
methods. You can use the same name for several different subprograms as long as
their formal parameters differ in number, order, or data type family.

Example defines two subprograms with the same name, initialize. The procedures
initialize different types of collections. Because the processing in these two procedures
is the same, it is logical to give them the same name.

You can place the two initialize procedures in the same block, subprogram, package, or
object type. PL/SQL determines which procedure to invoke by checking their formal
parameters. The version of initialize that PL/SQL uses depends on whether you invoke
the procedure with a date_tab_typ or num_tab_typ parameter.

Example Overloading a Subprogram Name:-

DECLARE

TYPE date_tab_typ IS TABLE OF DATE INDEX BY PLS_INTEGER;

93
TYPE num_tab_typ IS TABLE OF NUMBER INDEX BY PLS_INTEGER;

hiredate_tab date_tab_typ;

sal_tab num_tab_typ;

PROCEDURE initialize (tab OUT date_tab_typ, n INTEGER) IS

BEGIN

FOR i IN 1..n LOOP

tab(i) := SYSDATE;

END LOOP;

END initialize;

PROCEDURE initialize (tab OUT num_tab_typ, n INTEGER) IS

BEGIN

FOR i IN 1..n LOOP

tab(i) := 0.0;

END LOOP;

END initialize;

BEGIN

initialize(hiredate_tab, 20); -- Invokes first (date_tab_typ) version

initialize(sal_tab, 50); -- Invokes second (num_tab_typ) version

END;

Restrictions on Overloading:-

You cannot overload the following subprograms:

Standalone subprograms

Subprograms whose formal parameters differ only in mode; for example:

PACKAGE pkg IS

PROCEDURE X (p IN VARCHAR2);

PROCEDURE X (p OUT VARCHAR2);

END pkg;

Subprograms whose formal parameters differ only in subtype; for example:

PACKAGE pkg IS

PROCEDURE X (p INTEGER);

PROCEDURE X (p REAL);

94
END pkg;

INTEGER and REAL are subtypes of NUMBER, so they belong to the same data type
family.

Functions that differ only in return value data type, even if the data types are in
different families; for example:

PACKAGE pkg IS

FUNCTION f (p INTEGER) RETURN BOOLEAN;

FUNCTION f (p INTEGER) RETURN INTEGER;

END pkg;

Stored Program Units :-

A subprogram created at schema level is a standalone stored subprogram. You


create it with the CREATE PROCEDURE or CREATE FUNCTION statement. It is
stored in the database until you drop.

Stored Procedures:-

Syntax:-

CREATE OR REPLACE PROCEDURE [<Schema>.]<ProcedureName>

(<Argument> {IN, OUT, INOUT} <dataType>,….) {IS,AS}

<Variable Declarations>;

<Constant Declarations>;

BEGIN

<Body code block>;

EXCEPTION

<Exception code block>;

END;

Arguments:-To make a procedure dynamic it can be passed arguments/parameters


before execution . A Procedure then changes the way it works depending upon the
parameters passed prior to its execution.

Example:-

Create a procedure to increment particular employee salary by particular


amount.

SQL>ed proc1 ;

CREATE OR REPLACE PROCEDURE


raise_salary(e IN number,amt IN number )

95
IS
BEGIN
UPDATE emp SET sal = sal + amt WHERE empno = e ;
END;
To create procedure :-

SQL>@proc1 ;

Procedure created (compiled + stored in db)

To execute procedure :-

After creating procedure , procedure can be executed from SQL prompt,another PL/SQL
block and from any front-end application Java or .net Aplications

Executing From SQL prompt:-

SQL>EXECUTE raise_salary(7844,1000);

Executing from another pl/sql block :-

Write a pl/sql block to increment particular employee salary by particular amount ?

DECLARE
veno emp.empno%type;
vamt number(4);
BEGIN
veno := &eno;
vamt := &amount;
raise_salary(veno,vamt); /* call to the procedure */
END;
Using OUT parameter :-

An OUT parameter lets you return values to the caller of a subprogram. Inside the
subprogram, an OUT parameter acts like a variable. That means you can use an OUT

formal parameter as if it were a local variable. You can change its value or reference
the value in any way, as the following example shows:

CREATE OR REPLACE PROCEDURE


raise_salary(e IN number,amt IN number,s OUT number )
IS
BEGIN
UPDATE emp SET sal = sal + amt WHERE empno = e
RETURNING sal INTO s ;
END;
The actual parameter that corresponds to an OUT formal parameter must be a
variable, it cannot be a constant or an expression. For example, the following
procedure call is illegal:
raise_salary(7499, 1000,5000) ; --causes error

96
because the third parameter should not be a constant or expression , it should be a
variable.

Calling block:-

Write a pl/sql block to increment particular employee salary by particular amount after
increment if salary exceeds 5000 then cancel that increment ?

DECLARE
veno emp.empno%type;
vamt number(4);
vsal emp.sal%type;
BEGIN
veno := &eno;
vamt := &amount;
raise_salary(veno,vamt,vsal); /* call to the procedure */
if vsal >5000 then
rollback;
else
commit;
end if;
END;
Positional Versus Named Notation for Parameters:-

When calling a subprogram, you can write the actual parameters using either positional
or named notation. That is, you can indicate the association between an actual and
formal parameter by position or name.

PROCEDURE credit_acct (acct_id INTEGER, amount REAL) IS


BEGIN
UPDATE accts SET bal = bal + amount WHERE acct_no = acct_id;
END;
Calling block :-

DECLARE
acct INTEGER;
amt REAL;
BEGIN
acct := &account;
amt := &amount;
credit_acct(acct, amt); -- positional notation
credit_acct(amount => amt, acct_id => acct); -- named notation
credit_acct(acct_id => acct, amount => amt); -- named notation
credit_acct(acct, amount => amt); -- mixed notation
END;
Using positional notation :-

97
The first procedure call uses positional notation. The PL/SQL compiler associates the
first actual parameter, acct, with the first formal parameter, acct_id. And, the compiler
associates the second actual parameter, amt, with the second formal parameter,
amount.

Using Named Notation

The second procedure call uses named notation. An arrow (=>) serves as the
association operator, which associates the formal parameter to the left of the arrow
with the actual parameter to the right of the arrow.

The third procedure call also uses named notation and shows that you can list the
parameter pairs in any order. So, you need not know the order in which the formal
parameters are listed.

Using mixed notation :-

The fourth procedure call shows that you can mix positional and named notation. In
this case, the first parameter uses positional notation, and the second parameter uses
named notation. Positional notation must precede named notation. The reverse is not
allowed. For example, the following procedure call is illegal:

credit_acct(acct_no => acct, amt); -- illegal

Declaring parameters with DEFAULT values :-

To assign a default value to a parameter the assignment operator [:=] or the DEFAULT
keyword can be used.

<Argument1> IN Varchar2 := ‘ORACLE CORPORATION’;

<Argument2> IN Varchar2 DEFAULT ‘ORACLE CORPORATION’;

NOTE:- only IN parameters can be declared with DEFAULT values

PROCEDURE create_dept (
new_dname VARCHAR2 DEFAULT 'TEMP',
new_loc VARCHAR2 DEFAULT 'TEMP') IS
BEGIN
INSERT INTO dept
VALUES (deptno_seq.NEXTVAL, new_dname, new_loc);
END;
If an actual parameter is not passed, the default value of its corresponding formal
parameter is used. Consider the following calls to create_dept:
create_dept;
create_dept('MARKETING');
create_dept('MARKETING', 'NEW YORK');

98
The first call passes no actual parameters, so both default values are used. The second
call passes one actual parameter, so the default value for new_loc is used. The third
call passes two actual parameters, so neither default value is used.
Usually, you can use positional notation to override the default values of formal
parameters. However, you cannot skip a formal parameter by leaving out its actual
parameter. For example, the following call incorrectly associates the actual parameter
'NEW YORK' with the formal parameter new_dname:
create_dept('NEW YORK'); -- incorrect
You cannot solve the problem by leaving a placeholder for the actual parameter. For
example, the following call is not allowed:
create_dept( , 'NEW YORK'); -- not allowed
In such cases, you must use named notation, as follows:
create_dept(new_loc => 'NEW YORK');
PRAGMA AUTONOMOUS_TRANSACTION :-

If procedure declared with PRAGMA AUTONOMOUS_TRANSACTION then


transaction started in procedure executed as a separate transaction but not executed
as a part of transaction started in main program, commit and rollback command in
procedure doesn’t affect transaction started in main program.
Advantages:-

Once started , an autonomous transactin is fully independent, it shares no locks


,resources , or commit dependencies with the main transaction. A calling application
does not need to know whether transaction done by the stored procedure succeeded or
failed.

create or replace procedure


raise_salary(e IN number , amt IN number)
IS
pragma autonomous_transaction;
begin
update emp set sal=sal+amt where empno=e;
commit;
end;
calling block :-

begin

99
update emp set sal=sal+1000 where empno=7844; MT begins

raise_salary(7566,1000) ; AT committed

rollback; MT ends with rollback;

end;

result:-

7844 update is cancelled


7566 update is committed
Procedure returns single record :-

Create a procedure that accepts empno and returns employee record ?

CREATE OR REPLACE PROCEDURE


getEmployee(e IN NUMBER, r OUT emp%rowtype)
IS
BEGIN
SELECT * INTO r FROM emp WHERE empno = e;
END;
Calling block :-

DECLARE
veno emp.empno%type;
rec emp%rowtype;
BEGIN
veno := &empno;
getEmployee(veno,rec);
DBMS_OUTPUT.PUT_LINE(rec.ename || ‘ ‘|| rec.sal);
END;
Procedure returns multiple records :-

Create a procedure that accepts dept no and should return employee list working for
that dept

CREATE OR REPLACE PROCEDURE


getEmployeeList(d IN NUMBER, c OUT SYS_REFCURSOR)
IS
BEGIN
OPEN c FOR SELECT * FROM emp WHERE deptno = d;
END;
Calling block :-

DECLARE
Vdno dept.deptno%type;

100
c SYS_REFCURSOR;
r emp%rowtype;
BEGIN
vdno := &deptno;
getEmployeeList(vdno,c);
LOOP
FETCH c into r;
EXIT WHEN c%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(r.ename|| ‘ ‘||r.sal);
END LOOP;
CLOSE c;
END;
Passing Large Data Structures with the NOCOPY Compiler Hint:-

Starting with oracle 8i , PL/SQL offers an option for definition of parameters th NOCOPY

Clause. NOCOPY is a hint to the compiler about how you would like the PL/SQL engine
to work with the data being passed as an OUT or IN OUT parameters .There are two
ways to pass parameter values 1 by reference 2 by value

By Reference :-

When an actual parameter is passed by reference , it means that a pointer to the


actual parameter is passed to the corresponding formal parameter. So, both
parameters reference the same memory location, which holds the value of the actual
parameter.

By Value :-

When an actual parameter is passed by value ,the value of the actual parameter is
copied to the corresponding formal parameter. If the program then terminates without
an exception, the formal parameter value is copied back to the actual parameter. If an
error occurs , the changed values are not copied back to the actual parameter.

Parameter passing in PL/SQL without the use of NOCOPY follows these rules

Parameter Mode behavior

IN By Reference

OUT By Value

IN OUT By Value

By default, the OUT and IN OUT parameters are passed by value. That is, the value of
OUT and IN OUT actual parameter is copied into the corresponding formal parameter.

101
Then, if the subprogram exits normally, the values assigned to the OUT and IN OUT
formal parameters are copied into the corresponding actual parameters.

When the parameters hold large data structures such as collections, records, and
instances of object types, all this copying slows down execution and uses up memory.
To prevent that, you can specify the NOCOPY hint, which allows the PL/SQL compiler to
pass OUT and IN OUT parameters by reference.

CREATE OR REPLACE PACKAGE EMPLOYEE


As
Type emptab is table of emp%rowtype;
End;
/

CREATE OR REPLACE PROCEDURE


getEmployeeList(d IN NUMBER, r out NOCOPY employee.emptab)
IS
Begin
SELECT * INTO r FROM emp WHERE deptno = d;
End;
Impact of NOCOPY :-

NOCOPY can improve the performance of programs with IN OUT or OUT parameters
but if program terminates with an unhandled exception you cannot trust the values in a
NOCOPY actual parameter. Use NOCOPY hint when you know that you have a
performance problem relating to your parameter passing

Invoking External Subprograms:-

Although PL/SQL is a powerful, flexible language, some tasks are more easily done in
another language. Low-level languages such as C are very fast. Widely used languages
such as Java have reusable libraries for common design patterns.

You can use PL/SQL call specifications to invoke external subprograms written in other
languages, making their capabilities and libraries available from PL/SQL. For example,
you can invoke Java stored procedures from any PL/SQL block, subprogram, or
package.

If the following Java class is stored in the database

import java.sql.*;

import oracle.jdbc.driver.*;

public class Adjuster {

public static void raiseSalary (int empNo, float percent)

102
throws SQLException {

Connection conn = new OracleDriver().defaultConnection();

String sql = "UPDATE employees SET salary = salary * ?

WHERE employee_id = ?";

try {

PreparedStatement pstmt = conn.prepareStatement(sql);

pstmt.setFloat(1, (1 + percent / 100));

pstmt.setInt(2, empNo);

pstmt.executeUpdate();

pstmt.close();

} catch (SQLException e)

{System.err.println(e.getMessage());}

The class Adjuster has one method, which raises the salary of an employee by a given
percentage. Because raiseSalary is a void method, you publish it as a procedure using
the call specification shown in below Example and then can invoke the procedure
raise_salary from an anonymous PL/SQL block.

Invoking an External Procedure from PL/SQL:-

CREATE OR REPLACE PROCEDURE raise_salary (empid NUMBER, pct NUMBER)

AS LANGUAGE JAVA

NAME 'Adjuster.raiseSalary(int, float)';

DECLARE

emp_id NUMBER := 120;

percent NUMBER := 10;

BEGIN

-- get values for emp_id and percent

raise_salary(emp_id, percent); -- invoke external subprogram

END;

Stored Functions:-

103
A function is a subprogram that computes a value. Functions and procedures
structured alike except that functions return values using RETURN statement.

Syntax :-

CREATE OR REPLACE FUNCTION [<Schema>.]<FunctionName>


(<Argument> {IN, OUT, INOUT} <DataType>,…)
RETURN <DataType> {IS, AS}
<Variable Declarations>;
<Constant Declarations>;
BEGIN
<PL/SQL Sub Program Body code block>;
EXCEPTION
<Exception PL/SQL code block>;
END;
Example:-

Create a function that accepts account number and should return balance ?

SQL>ED fun1 ;

CREATE OR REPLACE FUNCTION


getBalance(a IN NUMBER) RETURN NUMBER IS
vbal acct_master.bal%type;
BEGIN
SELECT bal INTO vbal FROM acct_master WHERE accno=a;
RETURN vbal;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RETURN NULL;
END;
To create function :-

SQL>@FUN1

Function created (compiled + stored in db)

After creating function the function can be executed from

Select statement
Sqlprompt
Another pl/sql block
Executing from SELECT stmt:-

SQL>SELECT getBalance(1000) FROM DUAL;

Scenario:-

104
CUST_S

CUST_ID CUST_NAME
1 SACHIN RAMESH TENDULKAR
2 MAHINDRA SINGH DHONI
CUST_T

CUST_ID FIRST_NAME MID_NAME LAST_NAME


1 SACHIN RAMESH TENDULKAR
2 MAHINDRA SINGH DHONI
Create required functions and procedures to copy data from CUST_S to CUST_T

1 Create a function to accept name of the person and should return first_name :-

CREATE OR REPLACE FUNCTION first_name(n IN VARCHAR2)


IS
vfname varchar2(20);
BEGIN
vfname := SUBSTR(n , 1, INSTR(n, ‘ ‘)-1);
RETURN vfname;
END;
2 Create a function to accept name of the person and should return last_name:-

CREATE OR REPLACE FUNCTION last_name(n IN VARCHAR2)


IS
vlname varchar2(20);
BEGIN
vlname := SUBSTR(n , INSTR(n,’ ‘,-1,1)+1);
RETURN vlname;
END;
3 Create a function to accept name of the person and should return mid_name :-

CREATE OR REPLACE FUNCTION mid_name(n IN VARCHAR2)


IS
vfname varchar2(20);
vlname varchar2(20);
vmname varchar2(20);
BEGIN
vfname := first_name(n);
vlname := last_name(n);
vmname := TRIM(LTRIM(RTRIM(n,vlname),vfname));
RETURN vmname;
END;

105
4 Create procedure to copy data from CUST_S to CUST_T :-

CREATE OR REPLACE PROCEDURE copy_custs_custt


IS
CURSOR c1 IS SELECT * FROM cust_s;
vfname varchar2(20);
vmname varchar2(20);
vlname varchar2(20);
BEGIN
FOR r IN c1
LOOP
vfname := first_name(r.cust_name);
vmname := mid_name(r.cust_name);
vlname := last_name(r.cust_name);
INSERT INTO cust_t VALUES(r.cust_id,vfname,vmname,vlname);
END LOOP;
END;
Scenario:-

PRODUCTS ORDERS

PRODID PNAME PRICE ORDID PRODID QTY


1 A 100 1000 1 20
2 B 50 1000 2 50
3 C 150 1001 1 20
Create a function to calculate total bill of a particular
order ?

CREATE OR REPLACE FUNCTION total_bill(d IN NUMBER) RETURN NUMBER


IS
CURSOR c1 IS SELECT P.PRODID,P.PRICE,O.QTY
FROM ORDERS O,PRODUCTS P
WHERE O.PRODID = P.PRODID AND O.ORDID=d ;
vvalue NUMBER;
vtbill NUMBER := 0;
BEGIN
FOR r IN c1
LOOP
vvalue := r.qty * r.price
vtbill := vtbill + vvalue;
END LOOP;

106
RETURN vtbill;
END;
Table Functions:-

Table functions are functions that produce a collection of rows (either a nested table or
a varray) that can be queried like a database table. You use a table function like the
name of a database table, in the FROM clause of a query.

Execution of a table function can be parallelized, and returned rows can be streamed
directly to the next process without intermediate staging. Rows from a collection
returned by a table function can also be pipelined—that is, iteratively returned as they
are produced instead of in a batch after all processing of the table function's input is
completed.

Streaming, pipelining, and parallel execution of table functions can improve


performance:

Data processing with unparallelized and unpipelined table functions:-

Data processing using pipelining and parallel execution:-

SQL>create type array as table of number ;


Suppose we needed three rows for something. We can do that using table function
create function
gen_numbers(n in number default null) return array
PIPELINED
as
begin
for i in 1 .. nvl(n,999999999)
loop
pipe row(i);
end loop;
return;
end;
Executing Table Function :-

107
SQL>select * from TABLE(gen_numbers(3));
COLUMN_VALUE
------------
1
2
3
Using the PL/SQL Function Result Cache:-
The PL/SQL function result caching means for caching the results of PL/SQL functions
in a System global area (SGA), which is available to every session that runs your
application. The caching mechanism is both efficient and easy to use, and it relieves
you of the burden of designing and developing your own caches and cache-
management policies.
To enable result-caching for a function, use the RESULT_CACHE clause. When a result-
cached function is invoked, the system checks the cache. If the cache contains the
result from a previous call to the function with the same parameter values, the system
returns the cached result to the invoker and does not re execute the function body. If
the cache does not contain the result, the system executes the function body and adds
the result (for these parameter values) to the cache before returning control to the
invoker.
Note:-
If function execution results in an unhandled exception, the exception result is not
stored in the cache. The cache can accumulate very many results—one result for every
unique combination of parameter values with which each result-cached function was
invoked. If the system needs more memory, it ages out (deletes) one or more cached
results.
You can specify the database objects that are used to compute a cached result, so that
if any of them are updated, the cached result becomes invalid and must be
recomputed. The best candidates for result-caching are functions that are invoked
frequently but depend on information that changes infrequently or never.
CREATE OR REPLACE FUNCTION get_hire_date
(emp_id NUMBER, fmt VARCHAR) RETURN VARCHAR
RESULT_CACHE RELIES_ON (HR.EMPLOYEES)
IS
date_hired DATE;
BEGIN
SELECT hire_date INTO date_hired
FROM HR.EMPLOYEES
WHERE EMPLOYEE_ID = emp_id;
RETURN TO_CHAR(date_hired, fmt);
END;
/

108
If the result for get_hire_date(7844,’mm/dd/yy’) is already in the result cache, the
result is returned from the cache; otherwise, the result is computed and added to the
cache. Because the RELIES_ON clause specifies HR.EMPLOYEES, any update to
EMPLOYEES invalidates all cached results for get_hire_date relieving you of
programming cache invalidation logic everywhere that EMPLOYEES might change.
Destroying Procedure or Functions:-

A procedure or function can be dropped using the DROP command.

Syntax:-

DROP PROCEDURE <ProcedureName>;

Example:-

SQL>DROP PROCEDURE raise_salary;

Syntax:-

DROP FUNCTION <FunctionName>;

Example:-

SQL>DROP FUNCTION getBalance;

Getting Information About Procedures Or Functions:-

USER_OBJECTS

USER_PROCEDURES

USER_SOURCE

Difference between PROCEDURES and FUNCTIONS:-

PROCEDURE FUNCTION

Need not to return a value must return a value

May return more than one value can return only one value

Returns values using OUT parameter returns value using RETURN expr

Cannot be called from SELECT stmt. Can be called from SELECT stmt.

used to perform DML operations. used to perform computations.

Frequently Asked Questions :-

Is there a limit on the size of a PL/SQL block?

Answer: Currently, the maximum parsed/compiled size of a PL/SQL block is 64K and
the maximum code size is 100K.

What is a stored procedure?

A stored procedure is a named PL/SQL block stored in db and performs some operation
over database

109
What are advantages of Stored Procedures?

Extensibility, Modularity, Reusability, Maintainability and Precompiled

What are the modes of parameters that can be passed to a procedure?

IN,OUT,IN-OUT parameters.

What is Overloading of procedures?

The Same procedure name is repeated with parameters of different datatypes and
parameters in different positions, varying number of parameters is called overloading
of procedures.

What are actual and formal parameters ?

Actual Parameters : Subprograms pass information using parameters.The variables or


expressions referenced in the parameter list of a subprogram call are actual
parameters.

Formal Parameters : The variables declared in a subprogram specification and


referenced in the subprogram body are formal parameters.

What are the types of Notation ?

Position, Named, Mixed and Restrictions.

If I have an execute privilege on a procedure in another users schema, can I


execute his procedure even though I do not have privileges on the tables
within the procedure ?

Answer: Yes

What are pseudo-columns? How can you use pseudo-columns in procedural


statements?

Pseudo-columns are not actual columns in a table but they behave like columns. They
are used in SQL statements to retrieve specific information. PL/SQL recognizes pseudo-
columns as a part of SQL statements but they cannot be used directly in a procedural
language.

The following pseudo-columns are used in SQL:

=> ROWID

=> ROWNUM

=> LEVEL

=> CURRVAL

=> NEXTVAL.

When does a subprogram need recompilation?

110
When any database object associated with the subprogram changes, the program
needs to be recompiled. When the program is executed the next time, after the object
is changed, the system will automatically perform runtime recompilation.

To avoid this performance overhead, it is better to explicitly recompile the subprogram


using the ALTER [PROCEDURE | FUCNTION] command, as shown in the following
statement: ALTER PROCEDURE <NAME> compile;

Which datatypes can be used for parameters within a procedure?

Explicit datatypes, %TYPE and %ROWTYPE, without size specification can be used for
parameters in a procedure.

Can stand-alone programs be overloaded?

No, stand-alone programs cannot be overloaded; however, packaged sub-programs


can be overloaded within the same package.

How can a parameter be initialized in a procedure?

The IN OUT parameter mode can be used to initialize a parameter in a procedure,


as any value can be initially assigned to this parameter when the procedure is called
from the calling environment. The value of this parameter can be changed during the
execution of the procedure and the result can be returned back to the calling
environment from the procedure.

Can default values be assigned to IN OUT parameters in a procedure?

No, default values cannot be assigned to IN OUT parameters only IN parameters


can be assigned default values.

Suppose a procedure proc__calc_student_marks inserts the values into the


student_total_marks table. If the table is modified by adding another column,
what needs to be done to make the procedure valid?

Oracle recompiles the procedure automatically when the procedure is invoked.


Therefore, no action has to be taken if the INSERT statement is using column names.
However, if the INSERT statement is just adding values without using the column
names, then the procedure has to be modified, as another column has been added.

Can DDL commands be used within a procedure? If so, then how?

DDL commands, such as create, alter, revoke, and grant, cannot be used directly
within the procedural language block. They can only be called using built-in Native
Dynamic SQL or Dynamic SQL Package, which allows the execution of the DDL
statements at runtime.

What is the scope of the procedure defined within a PL/SQL block?

111
The scope of any procedure that is defined within the PL/SQL block will be limited as it
can be called only within the PL/SQL block and not by any other procedure or calling
environment.

Which privileges are required to execute a subprogram owned by another


user?

To execute a subprogram owned by another user, the user must be granted either the
EXECUTE privilege on a procedure or function or the EXECUTE ANY PROCEDURE system
privilege.

What happens if an exception is unhandled within a procedure?

If an exception is unhandled within a procedure, the control is passed back to the


calling environment. Any DML statements, which were executed within the procedure,
are rolled back.

What is the significance of AUTHID property while creating a procedure?

The AUTHID property affects the name resolution and privilege checking of SQL
statements at runtime; however, it does not affect the compilation, and has no
meaning for blocks that have no code, such as collection types.

• Any procedure may be created with its AUTHID property set to either the
DEFINER or the INVOKER rights.

• If a procedure is created with the DEFINER rights, then the user executing the
procedure need not have an access to database objects, which the procedure is
accessing.

• However, if a procedure is created with the INVOKER rights, then the user must
have access rights to all the objects that the procedure is accessing.

How can a compiled object code be viewed?

Compiled object code cannot be viewed, as it is not accessible to the user.

Can an IN parameter be assigned a value within a procedure?

No, an IN parameter cannot be assigned values within a procedure.

What are default parameter passing methods?

By default oracle uses byref mechanism for IN parameters

By default Oracle uses byval mechanism for OUT parameter.

What is NOCOPY hint ?

When OUT parameter is of type collection byval mechanism degrades performance to


improve performance declare OUT parameter with NOCOPY hint when OUT parameter
declared with NOCOPY hint then ORACLE uses byref mechanism.

112
What is difference between a PROCEDURE & FUNCTION?

A FUNCTION is alway returns a value using the return statement. A PROCEDURE may
return one or more values through parameters or may not return at all.

Explain how procedures and functions are called in a PL/SQL block ?

Function is called as part of an expression.

sal := calculate_sal ('a822');

procedure is called as a PL/SQL statement

calculate_bonus ('A822');

Can you have two functions with the same name in a PL/SQL block ?

Yes.

Can you have two stored functions with the same name ?

No.

Can you call a stored function in the constraint of a table ?

No.

What is Over Loading and what are its restrictions ?

OverLoading means an object performing different functions depending upon the no.of
parameters or the data type of the parameters passed to it.

Can functions be overloaded ?

Yes.

Can 2 functions have same name & input parameters but differ only by return
datatype

No.

Can there be multiple RETURN statements within a function?

Yes, there can be multiple RETURN statements within a function but only one is
executed. After the value is returned, the control passes back to the calling
environment and function processing is stopped.

Can BOOLEAN datatype be used in functions that are called from SQL

statements?

No, BOOLEAN datatype cannot be used in functions that are called from SQL
statements.

When should a procedure and a function be used?

113
Procedure is used when there are more than one return values however, if there is
only one return value, then a function should be used. Although functions may have
more than one OUT parameters, it is considered as a poor programming style.

In addition, when a subprogram has to be invoked within an SQL statement, function


should be used.

How can the text of a procedure or function specification be viewed?

The text of a procedure or function specification can be viewed using the


USER_SOURCE data dictionary view.

What are the different parameter modes, which can be used in the function
specification?

The three different modes, IN, OUT, and IN OUT, can be used in the function
specification. However, it is recommended to use only the IN mode. This is because
functions are supposed to accept values from the calling environment and return a
single value.

Can you invoke a stored function or procedure from the Oracle Forms in the
database?

Yes, a stored procedure or function can be invoked from the Oracle Forms and the
result can be used for further programming.

Can a function be defined without a RETURN statement?

No, a function cannot be defined without a RETURN statement because it has to


return a value back to the calling environment. In case a RETURN statement is not
defined, runtime error is raised.

How can the parameter list for a function or procedure be displayed?

The parameter list for a function or procedure can be displayed using the DESCRIBE
command, given as follows:

What is the meaning of the following error statement: ORA-06575: Package or


function func_test is in an invalid state?

This error is displayed if the function is executed when it is in the invalid state.

The ORA-06575 error indicates that for some reason the function is not valid and needs
to be checked and compiled again.

What are the restrictions on functions that are called within SQL statements?

The functions that are called within SQL statements have the following restrictions:

a. They must be stored in a database.

b. They cannot modify any database table.

114
c. They can only take the IN parameter modes. The OUT and IN OUT parameter
modes are not allowed in a function.

d. They can only use valid SQL datatypes, such as NUMBER, VARCHAR2, and DATE.
However, they cannot use PL/SQL datatypes, such as BOOLEAN, RECORD, and TYPE.

e. The return type of a function should be of SQL datatype.

f. They cannot have the COMMIT and ROLLBACK statements.

h. They cannot have the ALTER SESSION and ALTER SYSTEM commands.

How can you debug a procedure or function for errors?

The procedures or functions can be debugged using the SHOW_ERROR command or


the DBMS_OUTPUT command within the procedure or function

Packages :-
A package is a schema object that groups logically related PL/SQL types, variables, and
subprograms. Packages usually have two parts a specification ("spec") and a body.
The spec holds public declarations, which are visible to stored subprograms and other
code outside the package. You must declare subprograms at the end of the spec after
all other items (except pragmas that name a specific function; such pragmas must
follow the function spec).

The body holds implementation details and private declarations, which are hidden from
code outside the package. Following the declarative part of the package body is the
optional initialization part, which holds statements that initialize package variables and
do any other one-time setup steps.

A PL/SQL package contains the following:

 Get and Set methods for the package variables, if you want to avoid letting


other subprograms read and write them directly.
 Cursor declarations with the text of SQL queries. Reusing exactly the same
query text in multiple locations is faster than retyping the same query each time
with slight differences. It is also easier to maintain if you must change a query
that is used in many places.

 Declarations for exceptions. Typically, you must be able to reference these from
different subprograms, so that you can handle exceptions within invoked
subprograms.

 Declarations for subprograms that invoke each other. You need not worry about
compilation order for packaged subprograms, making  them more convenient
than standalone stored subprograms when they invoke back and forth to each
other.

115
 Declarations for overloaded subprograms. You can create multiple variations of a
subprogram, using the same names but different sets of parameters.

 Variables that you want to remain available between subprogram calls in the
same session. You can treat variables in a package like global variables.

 Type declarations for PL/SQL collection types. To pass a collection as a


parameter between stored subprograms, you must declare the type in a package
so that both the invoking andinvoked subprogram can refer to it.

Package Sepcification:-
The package specification contains public declarations. The declared items are
accessible from anywhere in the package and to any other subprograms in the same
schema. 
For Example :- FUNCTION factorial(n NUMBER) RETURN NUMBER

That is all the information needed to invoke the function. You need not consider its
underlying implementation (whether it is iterative or recursive for example).

If a spec declares only types, constants, variables, exceptions then the package body
is unnecessary. Only subprograms and cursors have an underlying implementation. 

Understanding the PL/SQL Package Body:-

The package body contains the implementation of every cursor and subprogram
declared in the package spec. Subprograms defined in a package body are accessible
outside the package only if their specs also appear in the package spec. If a
subprogram spec is not included in the package spec, that subprogram can only be
invoked by other subprograms in the same package. A package body must be in the
same schema as the package spec.

Advantages of Packages :-

Modularity

Packages let you encapsulate logically related types, items, and subprograms in a
named PL/SQL module. Each package is easy to understand, and the interfaces
between packages are simple, clear, and well defined. This aids application
development.

Easier Application Design

When designing an application, all you need initially is the interface information in the
package specs. You can code and compile a spec without its body. Then, stored
subprograms that reference the package can be compiled as well. You need not define
the package bodies fully until you are ready to complete the application.

Information Hiding

116
With packages, you can specify which types, items, and subprograms are public
(visible and accessible) or private (hidden and inaccessible). For example, if a package
contains four subprograms, three might be public and one private. The package hides
the implementation of the private subprogram so that only the package (not your
application) is affected if the implementation changes. This simplifies maintenance and
enhancement. Also, by hiding implementation details from users, you protect the
integrity of the package.

Added Functionality

Packaged public variables and cursors persist for the duration of a session. They can be
shared by all subprograms that execute in the environment. They let you maintain data
across transactions without storing it in the database.

Better Performance

When you invoke a packaged subprogram for the first time, the whole package is
loaded into memory. Later calls to related subprograms in the package require no disk
I/O.

Packages stop cascading dependencies and avoid unnecessary recompiling. For


example, if you change the body of a packaged function, the database does not
recompile other subprograms that invoke the function; these subprograms only depend
on the parameters and return value that are declared in the spec, so they are only
recompiled if the spec changes.

Example :-

CREATE PACKAGE emp_bonus AS

PROCEDURE calc_bonus (date_hired employees.hire_date


%TYPE);

END emp_bonus;

CREATE PACKAGE BODY emp_bonus AS

-- the following parameter declaration raises an exception

-- because 'DATE' does not match employees.hire_date%TYPE

-- PROCEDURE calc_bonus (date_hired DATE) IS

-- the following is correct because there is an exact match

PROCEDURE calc_bonus(date_hired employees.hire_date%TYPE) IS

BEGIN

DBMS_OUTPUT.PUT_LINE ('Employees hired on ' || date_hired || '


get bonus.');

117
END;

END emp_bonus;

After writing the package, you can develop applications that reference its types, invoke
its subprograms, use its cursor, and raise its exception. When you create the package,
it is stored in the database for use by any application that has execute privilege on the
package.

Creating the emp_admin Package :-

Consider the following package, named emp_admin. The package specification declares
the following types, items, and subprograms:

Type EmpRecTyp

Cursor desc_salary

Exception invalid_salary

Functions hire_employee and nth_highest_salary

Procedures fire_employee and raise_salary

After writing the package, you can develop applications that reference its types,
invoke its subprograms, use its cursor, and raise its exception. When you create the
package, it is stored in the database for use by any application that has execute
privilege on the package.

Creating the emp_admin Package

-- create the audit table to track changes

CREATE TABLE emp_audit(date_of_action DATE, user_id VARCHAR2(20),

package_name VARCHAR2(30));

CREATE OR REPLACE PACKAGE emp_admin AS

-- Declare externally visible types, cursor, exception

TYPE EmpRecTyp IS RECORD (emp_id NUMBER, sal NUMBER);

CURSOR desc_salary RETURN EmpRecTyp;

invalid_salary EXCEPTION;

-- Declare externally callable subprograms

FUNCTION hire_employee (last_name VARCHAR2,

first_name VARCHAR2,

email VARCHAR2,

118
phone_number VARCHAR2,

job_id VARCHAR2,

salary NUMBER,

commission_pct NUMBER,

manager_id NUMBER,

department_id NUMBER)

RETURN NUMBER;

PROCEDURE fire_employee

(emp_id NUMBER); -- overloaded subprogram

PROCEDURE fire_employee

(emp_email VARCHAR2); -- overloaded subprogram

PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER);

FUNCTION nth_highest_salary (n NUMBER) RETURN EmpRecTyp;

END emp_admin;

CREATE OR REPLACE PACKAGE BODY emp_admin AS

number_hired NUMBER; -- visible only in this package

-- Fully define cursor specified in package

CURSOR desc_salary RETURN EmpRecTyp IS

SELECT employee_id, salary

FROM employees

ORDER BY salary DESC;

-- Fully define subprograms specified in package

FUNCTION hire_employee (last_name VARCHAR2,

first_name VARCHAR2,

email VARCHAR2,

phone_number VARCHAR2,

job_id VARCHAR2,

salary NUMBER,

commission_pct NUMBER,

manager_id NUMBER,

119
department_id NUMBER)

RETURN NUMBER IS new_emp_id NUMBER;

BEGIN

new_emp_id := employees_seq.NEXTVAL;

INSERT INTO employees VALUES (new_emp_id,

last_name,

first_name,

email,

phone_number,

SYSDATE,

job_id,

salary,

commission_pct,

manager_id,

department_id);

number_hired := number_hired + 1;

DBMS_OUTPUT.PUT_LINE('The number of employees hired is '

|| TO_CHAR(number_hired) );

RETURN new_emp_id;

END hire_employee;

PROCEDURE fire_employee (emp_id NUMBER) IS

BEGIN

DELETE FROM employees WHERE employee_id = emp_id;

END fire_employee;

PROCEDURE fire_employee (emp_email VARCHAR2) IS

BEGIN

DELETE FROM employees WHERE email = emp_email;

END fire_employee;

-- Define local function, available only inside package

FUNCTION sal_ok (jobid VARCHAR2, sal NUMBER) RETURN BOOLEAN IS

min_sal NUMBER;

120
max_sal NUMBER;

BEGIN

SELECT MIN(salary), MAX(salary)

INTO min_sal, max_sal

FROM employees

WHERE job_id = jobid;

RETURN (sal >= min_sal) AND (sal <= max_sal);

END sal_ok;

PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER) IS

sal NUMBER(8,2);

jobid VARCHAR2(10);

BEGIN

SELECT job_id, salary INTO jobid, sal

FROM employees

WHERE employee_id = emp_id;

IF sal_ok(jobid, sal + amount) THEN

UPDATE employees SET salary =

salary + amount WHERE employee_id = emp_id;

ELSE

RAISE invalid_salary;

END IF;

EXCEPTION -- exception-handling part starts here

WHEN invalid_salary THEN

DBMS_OUTPUT.PUT_LINE

('The salary is out of the specified range.');

END raise_salary;

FUNCTION nth_highest_salary (n NUMBER) RETURN EmpRecTyp IS

emp_rec EmpRecTyp;

BEGIN

OPEN desc_salary;

FOR i IN 1..n LOOP

121
FETCH desc_salary INTO emp_rec;

END LOOP;

CLOSE desc_salary;

RETURN emp_rec;

END nth_highest_salary;

BEGIN -- initialization part starts here

INSERT INTO emp_audit VALUES (SYSDATE, USER, 'EMP_ADMIN');

number_hired := 0;

END emp_admin;

-- invoking the package procedures

DECLARE

new_emp_id NUMBER(6);

BEGIN

new_emp_id := emp_admin.hire_employee ('Belden',

'Enrique',

'EBELDEN',

'555.111.2222',

'ST_CLERK',

2500,

.1,

101,

110);

DBMS_OUTPUT.PUT_LINE

('The new employee id is ' || TO_CHAR(new_emp_id));

EMP_ADMIN.raise_salary(new_emp_id, 100);

DBMS_OUTPUT.PUT_LINE('The 10th highest salary is '||

TO_CHAR(emp_admin.nth_highest_salary(10).sal) || ',

belonging to employee: ' ||

TO_CHAR(emp_admin.nth_highest_salary(10).emp_id));

emp_admin.fire_employee(new_emp_id);

122
-- you can also delete the newly added employee as follows:

-- emp_admin.fire_employee('EBELDEN');

END;

Private and Public Members in Package:-

In the package emp_admin, the package body declares a variable


named number_hired, which is initialized to zero. Items declared in the body are
restricted to use within the package. PL/SQL code outside the package cannot
reference the variable number_hired. Such items are called private.

Items declared in the spec of emp_admin, such as the exception invalid_salary, are


visible outside the package. Any PL/SQL code can reference the
exception invalid_salary. Such items are called public.

To maintain items throughout a session or across transactions, place them in the


declarative part of the package body. For example, the value ofnumber_hired is kept
between calls to hire_employee within the same session. The value is lost when the
session ends.

To make the items public, place them in the package specification. For
example, emp_rec declared in the spec of the package is available for general use.

Package to Implement Various Bank Transactions :-

ACCT_MASTER

ACCNO NAME BAL


1000 A 10000
1001 B 5000

ACCT_TRANS

TRID TTYPE TDATE TAMT ACCNO


1 W 10-JAN-12 1000 1000
2 D 10-JAN-12 2000 1001
3 W 11-JAN-12 1000 1001

Package specification:-

123
create or replace package bank
as
procedure new_acct(a number,n varchar2,b number);
procedure close_acct(a number);
function getBalance(a number) return number;
procedure credit(a number,amt number);
procedure debit(a number,amt number);
procedure fund_transfer(s number,t number,amt number);
function getTransList(a number,s date, e date) return sys_refcursor;
function check_acct_exist(a number) return Boolean;
end;

package body :-

create or replace package body bank


as
procedure new_acct(a number, n varchar2, b number)
is
begin
insert into acct_master values(a,n,b);
end new_acct;
procedure close_acct(a number)
is
begin
delete from acct_master where accno=a;
end close_acct;
function getBalance(a number) return number
is
vbal acct_master.bal%type;
begin
select bal into vbal from acct_master where accno=a;
return vbal;
end getBalance;
function getTrid return number /* private function */
is
vtrid acct_trans.trid%type;
begin
select nvl(max(trid),0)+1 into vtrid from acct_trans;

124
return vtrid;
end getTrid;
procedure credit(a number,amt number)
is
vtrid acct_trans.trid%type;
begin
update acct_master set bal=bal+amt where accno=a;
vtrid := getTrid;
insert into acct_trans values(vtrid,’D’,sysdate,amt,a);
end credit;
procedure debit(a number,amt number)
is
vtrid acct_trans.trid%type;
begin
update acct_master set bal=bal-amt where accno=a;
vtrid := getTrid;
insert into acct_trans values(vtrid,’W’,sysdate,amt,a);
end debit;
procedure fund_transfer(s number, t number, amt number)
is
begin
debit(s,amt);
credit(t,amt);
end fund_transfer;
function getTransList(a number , s date ,e date) return sys_refcursor
is
c1 sys_refcursor
begin
open c1 for select * from acct_trans
where accno=a and tdate between s and e;
return c1;
end getTransList;
function check_acct_exist(a number) return Boolean
is
vname acct_master.name%type;
begin
select name into vname from acct_master where accno=a;
return true;
exception

125
when no_data_found then
return false;
end check_acct_exist;
end;

calling block:-
write a pl/sql block to transfer amount from one account to another account ?

declare
vsaccno acct_master.accno%type;
vtaccno acct_master.accno%type;
vamt acct_trans.tamt%type;
vbal acct_master.bal%type;
begin
vsaccno := &saccno;
vtaccno := &taccno;
vamt := &amount;
if vsaccno = vtaccno then
raise_application_error(-20001,’accts should not be same’);
end if;
if bank.check_acct_exist(vsaccno)=false then
raise_application_error(-20001,’source acct does not exists’);
end if;
if bank.check_acct_exist(vtaccno)=false then
raise_application_error(-20001,’target acct does not exists’);
end if;
vbal := bank.getBalance(vsaccno);
if vamt > vbal then
raise_application_error(-20001,’insufficient balance’);
end if;
bank.fund_transfer(vsaccno,vtaccno,vamt);
exception
when others then
dbms_output.put_line(sqlerrm);
end;
overloading example :-
create or replace package employee
as
procedure getDetails(e number,n out varchar2);
procedure getDetails(e number, n out varchar2,s out number);

126
procedure getDetails(e number, n out varchar2,s out number,d out number);
end;
/
create or replace package employee
as
procedure getDetails(e number, n out varchar2)
is
begin
select ename into n from emp where empno=e;
end getDetails;
procedure getDetails(e number, n out varchar2,s out number)
is
begin
select ename,sal into n,s from emp where empno=e;
end getDetails;
procedure getDetails(e number, n out varchar2,s out number,d out number)
is
begin
select ename,sal,deptno into n,s,d from emp where empno=e;
end getDetails;
end;

Using the PL/SQL Function Result Cache:-

The PL/SQL function result caching means for caching the results of PL/SQL functions
in a System global area (SGA), which is available to every session that runs your
application. The caching mechanism is both efficient and easy to use, and it relieves
you of the burden of designing and developing your own caches and cache-
management policies.

To enable result-caching for a function, use the RESULT_CACHE clause. When a result-
cached function is invoked, the system checks the cache. If the cache contains the
result from a previous call to the function with the same parameter values, the system
returns the cached result to the invoker and does not re execute the function body. If
the cache does not contain the result, the system executes the function body and adds
the result (for these parameter values) to the cache before returning control to the
invoker.
Note:-
If function execution results in an unhandled exception, the exception result is not
stored in the cache. The cache can accumulate very many results—one result for every
unique combination of parameter values with which each result-cached function was

127
invoked. If the system needs more memory, it ages out (deletes) one or more cached
results.

You can specify the database objects that are used to compute a cached result, so that
if any of them are updated, the cached result becomes invalid and must be
recomputed. The best candidates for result-caching are functions that are invoked
frequently but depend on information that changes infrequently or never.

In Example the package department_pks declares and then defines a result-cached


function, get_dept_info, which returns the average salary and number of employees in
a given department. get_dept_info depends on the database table EMPLOYEES.

Declaration and Definition of Result-Cached Function:-

-- Package specification
CREATE OR REPLACE PACKAGE department_pks IS
TYPE dept_info_record IS RECORD (average_salary NUMBER,
number_of_employees NUMBER);
-- Function declaration
FUNCTION get_dept_info (dept_id NUMBER) RETURN dept_info_record
RESULT_CACHE;
END department_pks;
/
CREATE OR REPLACE PACKAGE BODY department_pks AS
-- Function definition
FUNCTION get_dept_info (dept_id NUMBER) RETURN dept_info_record
RESULT_CACHE RELIES_ON (EMPLOYEES)
IS
rec dept_info_record;
BEGIN
SELECT AVG(SALARY), COUNT(*) INTO rec
FROM EMPLOYEES
WHERE DEPARTMENT_ID = dept_id;
RETURN rec;
END get_dept_info;
END department_pks;
/

128
DECLARE
dept_id NUMBER := 50;
avg_sal NUMBER;
no_of_emp NUMBER;
BEGIN
avg_sal := department_pks.get_dept_info(50).average_salary;
no_of_emp := department_pks.get_dept_info(50).number_of_employees;
DBMS_OUTPUT.PUT_LINE('dept_id = ' ||dept_id);
DBMS_OUTPUT.PUT_LINE('average_salary = '|| avg_sal);
DBMS_OUTPUT.PUT_LINE('number_of_employees = ' ||no_of_emp);
END;
/
You invoke the function get_dept_info as you invoke any function. For example, the
following call returns the number of employees in department number 10:
department_pks.get_dept_info(10).number_of_employees;
The following call returns only the average salary in department number 10:
department_pks.get_dept_info(10).average_salary;
If the result for get_dept_info(10) is already in the result cache, the result is returned
from the cache; otherwise, the result is computed and added to the cache. Because the
RELIES_ON clause specifies EMPLOYEES, any update to EMPLOYEES invalidates all
cached results for department_pks.get_dept_info, relieving you of programming cache
invalidation logic everywhere that EMPLOYEES might change.
Oracle Supplied Packages :- the following are list of packages supplied by
oracle.these package members can be invoked from PL/SQL,SQL and JAVA.
DBMS_ALERT
DBMS_APPLICATION_INFO
DBMS_AQ
DBMS_AQADM
DBMS_DDL
DBMS_DESCRIBE
DBMS_JOB
DBMS_LOB
DBMS_LOCK
DBMS_OUTPUT
DBMS_PIPE
DBMS_RANDOM
DBMS_ROWID
DBMS_SESSION
DBMS_SHARED_POOL
DBMS_SPACE

129
DBMS_SQL
DBMS_STANDARD
DBMS_SYSTEM
DBMS_TRANSACTION
DBMS_UTILITY
UTL_FILE
UTL_RAW
UTL_REF
Droping package specification :-

SQL>DROP PACKAGE <packagename> ;

SQL>DROP PACKAGE BANK ;

The above command drops package specification and body.

SQL>DROP PACKAGE BODY BANK ;

The above command drops only package body but not package specification.

Frequently Asked Questions :-

Define Package ?

Explain Advantages of Packages ?

What is Package Specification ?

What is Package Body ?

Explain about overloading in package ?

Explain about hiding in package ?

What is RESULT_CACHED function ?

What is pragma restrict references ?

List some oracle supplied packages ?

Raise_application_error built-in belongs to which package ?

Which package is used to perform file operations ?

UTL_FILE package

Which package is used to execute dynamic sql command ?

DBMS_SQL package

Which package is used to schedule stored procedures ?

DBMS_SCHEDULE

Which package is used to work with LOBs ?

DBMS_LOB package.

130
Triggers:-
A trigger is one of the extensively used database objects. A trigger is a named program
unit that is stored in the database and fired (executed) in response to a specified
event. The specified event is associated with either a table, a view a schema, or the
database

Types of Triggers:-

 DML triggers created on TABLES / VIEWS


 DDL triggers created on Schema
 Database: Systems event triggers [Startup/Shutdown]
A trigger is commonly used to:

 Automatically generate derived column values


 Enforce complex business rules

 Provide auditing

 Maintain synchronous table replicates

 Gather statistics on table access

 Modify table data when DML statements are issued against views

 Publish information about database events, user events, and SQL


statements to subscribing applications

 Restrict DML operations against a table to those issued during regular


business hours

 Enforce security authorizations

 Prevent invalid transactions

Guidelines for Designing Triggers:-

Use the following guidelines when designing triggers:

• Use triggers to guarantee that when a specific operation is performed, related


actions are performed.

• Do not define triggers that duplicate database features. For example, do not
define triggers to reject bad data if you can do the same checking through
constraints.

131
• Although you can use both triggers and integrity constraints to define and
enforce any type of integrity rule, Oracle strongly recommends that you use
triggers to constrain data input only in the following situations:

•To enforce referential integrity when child and parent tables are on different
nodes of a distributed database

•To enforce complex business rules not definable using integrity constraints

•Limit the size of triggers.

• If the logic for your trigger requires much more than 60 lines of PL/SQL code,
put most of the code in a stored subprogram and invoke the subprogram from
the trigger.

• The size of the trigger cannot exceed 32K.

• Use triggers only for centralized, global operations that must fire for the
triggering statement, regardless of which user or database application issues the
statement.

• If you use a LOGON trigger to monitor logons by users, include an exception-


handling part in the trigger, and include a WHEN OTHERS exception in the
exception-handling part. Otherwise, an unhandled exception might block all
connections to the database.

Creating Trigger:-

Syntax:-

CREATE OR REPLACE TRIGGER [<schema>.] <TriggerName>


{BEFORE, AFTER}
{DELETE, INSERT, UPDATE [OF <column>,……]}
ON [<schema>.] <TableName>
[REFERENCING {OLD AS old, NEW AS new}]
[FOR EACH ROW [WHEN <condition>]]
DECLARE
<Variable Declarations>;
<Constant Declarations>;
BEGIN
<PL/SQL Subprogram Body>;
END;

Parts of Trigger :-

A trigger has three basic parts:

 Trigger Event

 Trigger Restriction

132
 Trigger Action

Triggering event:-

A triggering event also called a triggering statement is a SQL statement, database


event or user event that cause a trigger to fire.

The trigger fires automatically when any of the following events occur:

 An INSERT, UPDATE or DELETE statement on a specific table or view

 A CREATE , ALTER or DROP statement on any schema object

 A database startup or instance shutdown

 A specific error message or any error message

 A user logon or logoff

Trigger Restriction:-

A trigger constraint specifies a Boolean expression that must be TRUE for the trigger to
fire. It allows conditional control over the execution of a trigger. A trigger restriction is
specified using a WHEN clause.

Trigger Action:-

The trigger action is a PL/SQL block that contains the code spec to be executed when
the trigger fires. The PL/SQL code block can hold SQL and PL/SQL statements, can
define PL/SQL language constructs and call stored procedures.

LEVELS OF TRIGGERS:-

DML Triggers can be Defined at Row Level and Statement Level

Row Level Triggers:-

Row level triggers are executed for each record affected by the DML operation.

For example, if a DELETE command deletes multiple rows of a table, a row trigger is
fired once for each row that is deleted. If the triggering affects no rows, the triggers is
not executed at all.

To make the trigger as row level trigger , declare with FOR EACH ROW option.

Statement Level Triggers:-

A statement trigger is executed once per the DML operation , irrespective of number
of records affected by DML operation.

Statement trigger should be used when a triggering statement affects rows in a table
but the processing required is completely independent of the number of rows affected.

Timed Trigger:-

133
When defining a trigger it is necessary to specify the trigger timing i.e. specifying
whether to perform the trigger action [i.e. execute Trigger code block] BEFORE or
AFTER the triggering statement. BEFORE and AFTER apply to both row and the
statement triggers.

Before Triggers:-

Indicates that the trigger will be fired before , the INSERT, UPDATE or DELETE
operation. It is generally used when the trigger action determines whether the
triggering statement should be allowed to complete. By using a BEFORE trigger for this
purpose, the unnecessary processing of the triggering statement and its eventual
rollback [incase an exception is raised in the trigger action] can be eliminated.

After Triggers:-

Indicates that the trigger will be fired after, the INSERT, UPDATE or DELETE
operations.The AFTER triggers are very useful when creating audit trail for database
tables.

Correlation Names:-

The data in the last SQL statement can be classified as NEW values and OLD values.

When the user fires an insert statement, the values of the columns included in the
insert statement can be read by using : NEW.columnname.

Similarly, if the user updates a record, the value before the modification can be read
using :OLD.columnname. The new values can be referenced using :NEW.columnname.

If user deletes a record then the record affected by delete can be read by using :OLD
variable.

Example 1:-

Create trigger to not to allow any DML operation on SUNDAY on EMP table

Create or replace trigger trg1


before insert or update or delete on emp
begin
If to_char(sysdate,’DY’)=’SUN’ then
Raise_application_error(-20001,’sunday not allowed’);
End if;
end;
Example 2:-

Create a trigger that fires every time the salary of an employee in the Emp table is
updated. This trigger should print the old, new and the difference in the salary after the
modification as:

134
Employee No :- 1

Old Salary :- 50000

New Salary :- 75000

Difference :- 25000

Solution:

CREATE OR REPLACE TRIGGER DispalyChangesInSalary


BEFORE UPDATE ON Emp
FOR EACH ROW
WHEN (NEW.EmployeeNo>0)
DECLARE
varDifference Number;
BEGIN
varDiffernce := :NEW.Sal -:OLD.Sal ;
DBMS_OUTPUT.PUT_LINE(‘Employee No:’|| :NEW.Emp);
DBMS_OUTPUT.PUT_LINE(‘Old salary:’ || :OLD.Sal);
DBMS_OUTPUT.PUT_LINE(‘NEW salary:’ || :NEW.Sal);
DBMS_OUTPUT.PUT_LINE(‘Difference:’ || varDiffernce);
END;
Example 3 :-

Create a trigger to not to allow more than 4 employees in a dept

CREATE OR REPLACE TRIGGER TRG3


BEFORE INSERT ON EMP
FOR EACH ROW
DECLARE
X NUMBER;
BEGIN
SELECT COUNT(*) INTO X FROM EMP WHERE DEPTNO = :NEW.DEPTNO;
IF X=4 THEN
RAISE_APPLICATION_ERROR(-20001,’MAX 4 EMPS’);
END IF;
END;
Test the trigger :-

SQL>INSERT INTO EMP(EMPNO,DEPTNO) VALUES(1,10);

1 ROW CREATED

SQL>INSERT INTO EMP(EMPNO,DEPTNO) VALUES(2,10);

1 ROW CREATED

135
SQL>INSERT INTO EMP(EMPNO,DEPTNO) VALUES(3,10);

1 ROW CREATED

SQL>INSERT INTO EMP(EMPNO,DEPTNO) VALUES(4,10);

1 ROW CREATED

SQL>INSERT INTO EMP(EMPNO,DEPTNO) VALUES(5,10);

ERROR :- ORA-20001 :- MAX 4 EMPS

Example 4 :-

JOBS

JOB LOSAL HISAL


CLERK 1000 2000
SALESMAN 1500 3000
MANAGER 2000 4000
Create a trigger that should raise error if new employee salary is out of range
(according to the salary range specified in jobs table).

CREATE OR REPLACE TRIGGER TRG4


BEFORE INSERT ON EMP
FOR EACH ROW
DECLARE
VLSAL JOBS.LSAL%TYPE;
VHSAL JOBS.LSAL%TYPE;
BEGIN
SELECT LSAL,HSAL INTO VLSAL,VHSAL FROM JOBS WHERE JOB=:NEW.JOB;
IF :NEW.SAL NOT BETWEEN VLSAL AND VHSAL THEN
RAISE_APPLICATION_ERROR(-20001,’SAL OUT OF RANGE’);
END IF;
END;
Testing Trigger :-

SQL>INSERT INTO EMP(EMPNO,ENAME,JOB,SAL,DEPTNO)


VALUES(1,’A’,’CLERK’,3000,10);
ERROR :- ORA-20001 :- Sal Out Of Range

INSTEAD OF Triggers:-

An INSTEAD OF trigger [normally defined on complex views] provides a transparent


way of modifying a view that cannot be modified directly through DML statements
such as INSERT, UPDATE and DELETE.

136
An INSTEAD OF trigger defined on a view is fired when an INSERT, UPDATE or
DELETE statement is executed against the view.

ORACLE executes INSTEAD OF TRIGGER instead of executing DML operation.

When fired, the INSTEAD OF trigger modifies the underlying tables appropriately.

An INSTEAD OF trigger defined on a view is always a row trigger i.e. the trigger is
fired for each modified row of the view.

INSTEAD OF triggers can be designed only for views and not for tables. The BEFORE
and AFTER options, cannot be used with INSTEAD OF triggers.

Example 5:-

A view named EmpDept is made up of the following columns:

EmpNo
Ename
Sal
DeptNo
DName
CREATE VIEW EmpDept AS
SELECT E.EmpNo, E.Ename, E.Sal, D.DeptNo,D.DName
FROM Emp E , Dept D
WHERE E.DeptNo = D.DeptNo;
Since the view is a complex view i.e. it holds columns from two tables using a JOIN,
data manipulation is not possible.

Example 5:-

Create a trigger that will help to achieve this.

CREATE OR REPLACE TRIGGER UpdateComplexView


INSTEAD OF INSERT ON EmpDept
FOR EACH ROW
BEGIN
INSERT INTO Emp(EmpNo,Ename,Salary)
VALUES(:NEW.EmpNo,:NEW.Ename,:NEW.Salary);
INSERT INTO Dept(DeptNo, Dname)
VALUES(:NEW.DeptNo, :NEW.DName);
END;
Test trigger :-

SQL>INSERT INTO EMPDEPT VALUES(1,’A’,5000,90,’PURCHASE’);

1 ROW CREATED.

Example 6:-

137
Similarly to allow deletes via view vwEmpdept, use the following trigger:

CREATE OR REPLACE TRIGGER DeleteComplexView


INSTEAD OF DELETE ON EmpDept
FOR EACH ROW
DECLARE
varEmployees Number;
BEGIN
DELETE FROM Emp WHERE EmpNo =: OLD.EmpNo;
SELECT COUNT(*) INTO varEmployees
FROM Emp WHERE Emp.DeptNo=:OLD.DeptNo;
/*checking if no employee exists in a particular department*/
IF varEmployees = 0 THEN
DELETE FROM Dept WHERE DeptNo =:OLD.Deptno;
END IF;
END;
/

Mutating Table Error:-

 The Mutating table error is a well-known problem encountered in development


environment.

ORA-04091: table <tablename> is mutating, Trigger /function may not see it

 The basic reason for this error is the way Oracle manages a read consistent view of
data. The error is encountered when a row-level trigger accesses the same table on
which it is based, while executing. The table is said to be mutating.

 Mutation will not occur if a single record is inserted in the table [using VALUES
caluse]. If bulk insertion is done or data is inserted from another table mutation will
occur.

The mutation is not only encountered during queries, but also for insert, updates and
deletes present in the trigger.

The following table explains various transactions scenarios that involve a trigger and
whether it is prone to generate the mutating error.

Operation Type Mutating

Insert Before/statement-level No

Insert After/statement-level No

Update Before/statement-level No

138
Update After/statement-level No

Delete Before/statement-level No

Delete After/statement-level No

Insert Before/row-level Single-row Multi-row

No Yes

Insert After/row-level Yes

Update Before/row-level Yes

Update After/row-level Yes

Delete Before/row-level Yes

Delete After/row-level Yes

Example:-

Write a trigger that fires every time the salary of an employee in the Employees table
is updated either due to an insert, update or delete. This trigger should print the total
salary of the available employees after the modification.

CREATE OR REPLACE TRIGGER DisplayTotalSalary


AFTER INSERT OR UPDATE OR DELETE ON Emp
FOR EACH ROW
DECLARE
varTotalSal Number;
BEGIN
SELECT SUM (Salary) INTO varTotalSal FROM Employees;
DBMS_OUTPUT.PUT(‘Total Salary:’ || varTotalSal);
END;
Test the trigger by issuing the following command:-

UPDATE Employees SET Salary =Salary*1.10 WHERE DeptNo=4;

ORA-4091 :- mutating table error

To avoid mutating table errors:

A row-level trigger must not query or modify a mutating table. However, correlation
names such as NEW and OLD cannot be accessed by the trigger.

A statement-level trigger must not query or modify a mutating table if the trigger is
fired as result of a CASCADE delete.

Auditing :-

Create a trigger to audit DML operations on EMP table into EMP_AUDIT table.

EMP_AUDIT

139
UNAME OPERATION OPERATION_TIME
SCOTT INSERT ---
SCOTT UPDATE ---
SCOTT DELETE --
CREATE OR REPLACE TRIGGER TRG6
AFTER INSERT OR UPDATE OR DELETE
ON EMP
BEGIN
IF INSERTING THEN
INSERT INTO EMP_AUDIT VALUES(USER,’INSERT’,SYSTIMESTAMP);
ELSIF UPDATING THEN
INSERT INTO EMP_AUDIT VALUES(USER,’UPDATE’,SYSTIMESTAMP);
ELSE
INSERT INTO EMP_AUDIT VALUES(USER,’DELETE’,SYSTIMESTAMP);
END IF;
END;
DDL Triggers:-

A DDL trigger is a database trigger whose triggering event is a Data Defining Language
[DDL] statement i.e. a DDL trigger fires when a DDL statement [such as a CREATE,
ALTER or DROP statement] is executed in the database or a particular schema.

Example 7:-

Create a DDL trigger to not allow any DDL operation on Sunday

CREATE OR REPLACE TRIGGER TRG7


BEFORE CREATE OR ALTER OR DROP
ON SCHEMA
BEGIN
IF TO_CHAR(SYSDATE,’DY’)=’SUN’ THEN
RAISE_APPLICATION_ERROR(-20001,’NOT TO DO ANY DDL ON SUNDAY’);
END IF;
END;
Example 8:-

The DBA desires to keep track of the user and the date on which objects are created or
dropped in the database.

Write a DDL trigger that inserts s record in the DDLAudit table. This table holds:

EventName
ObjectOwner

140
ObjectName
ObjectType
Operator
OperationDate
CREATE OR REPLACE TRIGGER DDLAudit
BEFORE CREATE OR DROP ON SCHEMA
BEGIN
INSERT INTO DDLAuditTrail(ora_Sysevent,Ora_Dict_Obj_Owner,
Ora_Dict_obj_Name, Ora_Dict_Obj_Type, USER, SYSDATE );
END ;
Invoking a Java Subprogram from a Trigger:-

CREATE OR REPLACE PROCEDURE Before_delete (Id IN NUMBER, Ename VARCHAR2)


IS language Java
name 'thjvTriggers.beforeDelete (oracle.sql.NUMBER, oracle.sql.CHAR)';
/
CREATE OR REPLACE TRIGGER Pre_del_trigger BEFORE DELETE ON Tab
FOR EACH ROW
CALL Before_delete (:OLD.Id, :OLD.Ename)
/
The corresponding Java file is thjvTriggers.java:
import java.sql.*
import java.io.*
import oracle.sql.*
import oracle.oracore.*
public class thjvTriggers
{
public state void
beforeDelete (NUMBER old_id, CHAR old_name)
Throws SQLException, CoreException
{
Connection conn = JDBCConnection.defaultConnection();
Statement stmt = conn.CreateStatement();
String sql = "insert into logtab values
("+ old_id.intValue() +", '"+ old_ename.toString() + ", BEFORE DELETE”);
stmt.executeUpdate (sql);
stmt.close();
return;
}
}

141
Database Level Triggers :-

These Triggers are created on Database and trigger event can be STARTUP or
SHUTDOWN.

Example 9:-

The DBA desires to daily track the user and the date when the database was shutdown.

Write a database trigger that inserts a record into the DBAAuditTrail

DBAAuditTrail

USER TIME

CREATE OR REPLACE TRIGGER DBAuditTrail

BEFORE SHUTDOWN ON DATABASE

BEGIN

INSERT INTO DBAuditTrail(USER,SYSTIMESTAMP);

END;

Connect to DBA account and SHUTDOWN the database

SQL>CONN SYSTEM/MANAGER

SQL>SHUTDOWN

Compound Trigger Example:-

Compound triggers [introduced in Oracle Database 11g] allow defining more than one
type of trigger in a single trigger.

Syntax:-

CREATE OR REPLACE TRIGGER <trigger-name>


FOR <trigger-action> ON <table-name>
COMPOUND TRIGGER
-- Global declaration.
g_global_variable VARCHAR2(10);
BEFORE STATEMENT IS
BEGIN
NULL; -- Do something here.
END BEFORE STATEMENT;
BEFORE EACH ROW IS
BEGIN

142
NULL; -- Do something here.
END BEFORE EACH ROW;
AFTER EACH ROW IS
BEGIN
NULL; -- Do something here.
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
NULL; -- Do something here.
END AFTER STATEMENT;
END <TRIGGER-NAME>;
Follows Clause:-

Oracle allows more than one trigger to be created for the same timing point, but it has
never guaranteed the execution order of those triggers. The Oracle 11g trigger syntax
now includes the FOLLOWS clause to guarantee execution order for triggers defined
with the same timing point. The following example demonstrates how follows clause
can be used.

CREATE OR REPLACE TRIGGER trg1


BEFORE INSERT ON EMP
FOR EACH ROW
BEGIN
DBMS_OUTPUT.put_line('TRIGGER 1 - Executed');
END;
Now create second trigger :-

CREATE OR REPLACE TRIGGER trg2


BEFORE INSERT ON EMP
FOR EACH ROW
FOLLOWS trg1 /* trg2 execution follows trg1 */
BEGIN
DBMS_OUTPUT.put_line('TRIGGER 2 - Executed');
END;
Destroying An Existing Trigger:-

Syntax:-

DROP TRIGGER <TriggerName>;

Example:-

SQL>DROP TRIGGER TRG1;

Disabling Trigger :-

143
Triggers can be disabled and enabled by using ALTER command.

SQL>ALTER TRIGGER TRG2 DISABLE;

SQL>ALTER TRIGGER TRG2 ENABLE;

We can also disable all the triggers created on a table

SQL>ALTER TABLE EMP DISABLE ALL TRIGGERS;

Querying Triggers Information :-

USER_TRIGGERS

ALL_TRIGGERS

DBA_TRIGGERS

Display list of triggers created by user ?

SELECT TRIGGER_NAME FROM USER_TRIGGERS;

What is trigger in oracle?

Triggers are constructs in PL/SQL that need to be just created and associated with a
table. Once they are created, when the table associated with it gets updated due to an
UPDATE, INSERT or a DELETE, the triggers get implicitly fired depending upon the
instructions passed to them

What are the types of triggers?

The types of triggers are:

• Row level triggers

• Statement level triggers

• BEFORE and AFTER triggers

nd trigger type.

What are triggering attributes?

Triggers can be fired based on the following criteria:

Category - (INSERT, DELETE, UPDATE) i.e. which kind of DML statement causes the
trigger to fire.

Timing – (BEFORE or AFTER) i.e. whether the trigger fires before the statement is
executed of after.

Level – (Row or Statement) i.e. whether it fires once for each row affected by trigger
statement or whether it fires once.

Disadvantages of trigger in oracle ?

144
Triggers can execute every time some field in database is updated. If a field is likely to
be updated often, it is a system overhead. It is not possible to track or debug triggers.

Difference between trigger and stored procedure

• A stored procedure can accept parameters while a trigger cannot.

• A trigger can’t return any value while stored procedures can.

• A trigger is executed automatically on some event while a stored procedure


needs to be explicitly called.

• Triggers are used for insertions, update and deletions on tables while stored
procedures are often using independently in the database.

• A trigger cannot be written in a stored procedure. However, the reverse is not


possible.

Explain Row level and statement level trigger ?

Answer:

A trigger if specified FOR EACH ROW; it is fired for each of the table being affected by
the triggering statement. For example if a trigger needs to be fired when rows of a
table are deleted, it will be fired as many times the rows are deleted.

If FOR EACH ROW is not specified, it is application to a statement and the trigger is
executed at a statement level.

What are cascading triggers?

At times when SQL statement of a trigger can fire other triggers. This results in
cascading triggers. Oracle allows around 32 cascading triggers. Cascading triggers can
cause result in abnormal behavior of the application

How many types of database triggers can be specified on a table? What are
they?

ROW/STATEMENT Insert Update Delete

Before Row o.k. o.k. o.k.

After Row o.k. o.k. o.k.

Before Statement o.k. o.k. o.k.

After Statement o.k. o.k. o.k.

145
Is it possible to use Transaction control Statements such a ROLLBACK or
COMMIT in Database Trigger? Why?

It is not possible. As triggers are defined for each table, if you use COMMIT of
ROLLBACK in a trigger, it affects logical transaction processing.

What happens if a procedure that updates a column of table X is called in a


database trigger of the same table?

Mutation of table occurs.

Write the order of precedence for validation of a column in a table ?

I. validation using Database triggers.

ii. validation using Integarity Constraints.

What are the values of :new and :old in Insert/Delete/Update Triggers ?

INSERT : new = new value, old = NULL

DELETE : new = NULL, old = old value

UPDATE : new = new value, old = old value

What are mutating triggers ?

A trigger firing a SELECT on the table on which the trigger is written.

What are constraining triggers ?

A trigger firing an Insert/Update on a table having referential integrity constraint on


the triggering table.

What are schema-level triggers?

Schema-level triggers are created on schema-level operations, such as create table,


alter table, drop table, rename, truncate, and revoke. These triggers prevent DDL
statements, provide security, and monitor the DDL operations.

Does a USER_OBJECTS view have an entry for a trigger?

Yes, the USER_OBJECTS view has one row entry for each trigger in the schema.

What are the system privileges that are required by a schema owner (user)
to create a trigger on a table?

A user must be able to alter a table to create a trigger on the table. The user must
own the table and either have the ALTER TABLE privilege on that table or have the
ALTER ANY TABLE system privilege. In addition, the user must have the CREATE
TRIGGER system privilege. User should have the CREATE ANY TRIGGER system
privilege to be able to create triggers in any other user account or schema.

146
A database-level event trigger can be created if the user has the ADMINISTER
DATABASE TRIGGER system privilege.

What is a database event trigger?

Trigger that is executed when a database event, such as startup, shutdown, or error,
occurs is called a database event trigger. It can be used to reference the attributes of
the event and perform system maintenance functions immediately after the database
startup.

How can you view the errors encountered in a trigger?

The USER_ERRORS view can be used to show all the parsing errors that occur in
a trigger during the compilation until they are resolved.

In what condition is it good to disable a trigger?

It is good to disable triggers during data load operations. This improves the
performance of the data loading activities. The data modification and manipulation that
the trigger would have performed has to be done manually after the data loading.

Does USER_TRIGGERS have entry for triggers with compilation errors?

Yes, USER_TRIGGERS have entries for all triggers that are created in the schema
with or without errors.

How can triggers be used for the table auditing?

Triggers can be used to track values for data operations on tables. This is done using
the old and new qualifiers within the trigger code. These two clauses help keep track of
the data that is being inserted, updated, or deleted in the table; and therefore,
facilitate in application auditing of DML statements. The audit trail can be written to a
user-defined table and audit records can be generated for both row-level and
statement-level triggers.

Which column of the USER_TRIGGERS data dictionary view displays the


database event that will fire the trigger?

The Description column of the USER_TRIGGERS view combines information from


many columns to display the trigger header, which includes the database event.

Which of the following events cannot be customized with triggers?

Answer:

A. INSERT INTO statement

B. CREATE TABLE statement in a schema

C. DELETE CASCADE CONSTRAINTS

D. SET PAUSE ON

147
Option D cannot be customized; whereas, all the other options can be customized
using triggers.

What are INSTEAD OF triggers?

The INSTEAD OF triggers are used in association with views. These triggers inform
the database of what actions are to be performed instead of the actions that invoked
the trigger. Therefore, the INSTEAD OF triggers can be used to update the underlying
tables, which are part of the views.

Is it possible to pass parameters to triggers?

No

Can triggers stop a DML statement from executing on a table?

Yes, triggers have the capability of stopping any DML statement from execution on a
table. Any logical business rule can be implemented using PL/SQL to block modification
on table data.

Can a SELECT statement fire a trigger?

No, a SELECT statement cannot fire a trigger. DML statements, such as INSERT,
UPDATE, and DELETE, can cause triggers to fire.

How can the performance of a trigger be improved?

• The performance of a trigger can be improved by using column names along


with the UPDATE clause in the trigger.

• This will make the trigger fire when that particular column is updated and
therefore, prevents unnecessary action of trigger when other columns are being
updated.

Can a view be mutating? If yes, then how?

• No, a view cannot be mutating like a table. If an UPDATE statement fires an


INSTEAD OF trigger on a view, the view is not considered to be mutating. If the
UPDATE statement had been executed on a table, the table would have been
considered as mutating.

What are the events on which a database trigger can be based?

Database triggers are based on system events and can be defined at database or
schema level. The various events on which a database trigger can be based are given
as follows:

=> Data definition statement on a database or schema object

=> Logging off or on of a specific user

=> Database shutdown or startup

148
=> On any specific error that occurs.

What is a mutating table?

• A mutating table is a table, which is in the state of transition. In other words, it


is a table, which is being updated at the time of triggering action.

• If the trigger code queries this table, then a mutating table error occurs, which
causes the trigger to view the inconsistent data.

Do triggers have restrictions on the usage of large datatypes, such as LONG


and LONG RAW?

• Triggers have restrictions on the usage of large data types as they cannot
declare or reference the LONG and LONG RAW data types and cannot use them even if
they form part of the object with which the trigger is associated.

• Similarly, triggers cannot modify the CLOB and BLOB objects as well; however,
they can reference them for read-only access.

Which data dictionary views have the information on the triggers that are
available in the database?

• The data dictionary views that have information on database triggers are given
as follows:

USER_OBJECTS — Contain the name and status of a trigger as well as the date

and time of trigger creation

USER_ERRORS— Contain the compilation error of a trigger

USER_ TRIGGERS— Contain the source code of a trigger

USER_ TRIGGER_COLS— Contain the information on columns used in triggers.

DML Error Logging

Oracle 10g introduced the DML error logging feature.

This feature is useful in situations such as:

Updating 30 million records. The update operation fails after 30 minutes just because
one of the record amongst those 30 million fails a check constraint.

An INSERT AS SELECT command fails on the row number 899 to 1000 just because
one column value is too large

The DML error logging feature allows adding a clause to the INSERT statement that
causes the 999 correct records to be inserted successfully and the one erroneous
record to be written out to a error logging table for resolving later.

Example:-

149
Create a table with a few constraints that can be violated for demonstration purpose.

CREATE TABLE StudentAttendance(

StudentNo Varchar2(2) PRIMARY KEY,

Attendance Varchar2(1);

Example:-

Write a PL/SQL program that inserts 100 records in the student Attendance table.

DECLARE
I Number;
BEGIN
I := 1;
WHILE i<=100
LOOP
INSERT INTO StudentAttendance (StudentNo, Attendance) VALUES (I, ‘p’);
DBMS_OUTPUT.PUT_LINE(‘Student No:’ || I);
I := I+1;
END LOOP;
END;

Because 100th time INSERT stmt generates error , so INSERTING 1 to 99 records into
table is cancelled.

Creating The Error Logging Table:-

Create an error logging table to hold the DML errors.

Oracle provides a built-in PL/SQL package named DBMS_ERRLOG, specifically for this
purpose.

BEGIN
DBMS_ERRLOG.CREATE_ERROR_LOG(‘StudentAttendance’, ‘ErrLogAttendance’);
END;
Logging An Error:-

Once the DML error logging table has been created for a particular table, DML errors
can be logged by adding an error logging clause to the DML statement.

Syntax:-

LOG ERRORS [INTO <Schema>.<Table>] [(<SimpleExpression>)

[REJECT LIMIT {Integer |UNLIMITED}]

Where,

Schema.Table is the name of the error logging table

150
REJECT LIMIT clause is technically optional, the default reject limit is zero. The
error logging clause is inefffective if a reject limit is not specified.

Simple Expression sub clause allows specifying a statement tag, which is


logged in the ORA_ERR_TAGS fields of the error logging table, to identify which
statement caused the error.

Example:-

DECLARE
I Number;
BEGIN
I := 1;
WHILE I<=100
LOOP
INSERT IINTO StudentAttendance(StudentNo,Attendance) VALUES (I, ‘p’);
LOG ERRORS INTO ErrLogStudentAttendance REJECT LIMIT 1;
DBMS_OUTPUT.PUT_LINE(‘StudentNo: ‘|| I);
I := I + 1;
END LOOP;
END;
Now since the errors are logged, the 99 correct records are populated in the
StudentAttendence table.

Oracle Supplied Packages :-

UTL_FILE package :-

With the UTL_FILE package, PL/SQL programs can read data from and write into
operating system text files.

Creating Directory Object :-

CONN SYSTEM / MANAGER

SQL>CREATE DIRECTORY dir1 AS ‘D:\NARESHIT’ ;

SQL>GRANT READ,WRITE ON DIRECTORY dir1 TO SCOTT;

Members of UTL_FILE package :-

FILE_TYPE :- is a Datatype , any variable declared with FILE_TYPE is a file variable.

F1 UTL_FILE.FILE_TYPE ;

FOPEN :- is a function used to open a file for read or write.

UTL_FILE.FOPEN(location ,filename, open_mode,max_linesize) ;

Modes can ‘r’ for read


‘w’ for write

151
‘a’ for append
F1 := UTL_FILE.FOPEN(‘DIR1’,’abc.txt’,’r’)

PUT_LINE :- is a procedure used to write a line to a file.

UTL_FILE.PUT_LINE(file , data ,autoflush);

UTL_FILE.PUT_LINE(F1,’HELLO WELCOME’);

GET_LINE :- is a procedure used to get a line from file.

UTL_FILE.GET_LINE(file , string variable,len);

UTL_FILE.GET_LINE(F1,s); /* reads a line from file and copies to s */

FCOPY :- is a procedure used to copy contents of one file to newly created file.

UTL_FILE.FCOPY(source_dir ,source_file, dest_dir ,dest_file , start_line , end_line);

FCLOSE :- procedure used to close a file.

UTL_FILE.FCLOSE(file);

UTL_FILE.FCLOSE(F1);

FCLOSE_ALL :- closes all files.

UTL_FILE.FCLOSE_ALL ;

Program to Write Data into Text file :-

DECLARE
F1 UTL_FILE.FILE_TYPE;
BEGIN
F1 := UTL_FILE.FOPEN(‘DIR1’,’abc.txt’,’w’);
UTL_FILE.PUT_LINE(F1,’HELLO’);
UTL_FILE.PUT_LINE(F1,’WELCOME’);
UTL_FILE.PUT_LINE(F1,’TO NARESH TECHNOLOGIES’);
UTL_FILE.FCLOSE(F1);
END;

After executing the above program under D:\NARESHIT directory abc.txt file is created
And 3 lines are added to that text file.
Program to read Data from Text file :-

DECLARE
F1 UTL_FILE.FILE_TYPE;
S VARCHAR2(1000);
BEGIN
F1 := UTL_FILE.FOPEN(‘DIR1’,’abc.txt’,’r’);
LOOP
UTL_FILE.GET_LINE(F1,S);

152
DBMS_OUTPUT.PUT_LINE(S);
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
UTL_FILE.FCLOSE(F1);
END;

After executing the above program , it prints following 3 lines

HELLO
WELCOME
NARESH IT
Program to write employee data into a text file :-

DECLARE
TYPE ETYPE IS TABLE OF EMP%ROWTYPE;
rec ETYPE;
F1 UTL_FILE.FILE_TYPE;
BEGIN
F1 := UTL_FILE.FOPEN(‘DIR1’,’emp.txt’,’w’);
SELECT * BULK COLLECT INTO rec FROM EMP;
FOR I IN rec.FIRST..rec.LAST
LOOP
UTL_FILE.PUT_LINE(F1,rec(I).empno||’ ,’||rec(I).ename||’,’||rec(I).sal);
END LOOP;
UTL_FILE.FCLOSE(F1);
END;
Program to read data from text file :-

DECLARE
F1 UTL_FILE.FILE_TYPE;
rec varchar2(1000);
BEGIN
F1 := UTL_FILE.FOPEN(‘DIR1’,’emp.txt’,’r’);
LOOP
UTL_FILE.GET_LINE(F1,rec);
DBMS_OUTPUT.PUT_LINE(rec);
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
UTL_FILE.FCLOSE(F1);
END;

153
Dynamic SQL :-

Dynamic SQL is a programming methodology for generating and executing SQL


statements at run time. It is useful when writing general-purpose and flexible
programs like ad hoc query systems, when writing programs that must execute DDL
statements, or when you do not know at compilation time the full text of a SQL
statement or the number or data types of its input and output variables.

PL/SQL provides two ways to write dynamic SQL:-

1 Native dynamic SQL, a PL/SQL language (that is, native) feature for building and
executing dynamic SQL statements.

2 DBMS_SQL package, an API for building, executing, and describing dynamic SQL
statements

Native dynamic SQL code is easier to read and write than equivalent code that uses the
DBMS_SQL package, and runs noticeably faster. However, to write native dynamic SQL
code, you must know at compile time the number and data types of the input and
output variables of the dynamic SQL statement. If you do not know this information at
compile time, you must use the DBMS_SQL package.

In PL/SQL, you need dynamic SQL in order to execute the following:-

• SQL whose text is unknown at compile time

For example, a SELECT statement that includes an identifier that is unknown at


compile time (such as a table name) or a WHERE clause in which the number of
subclauses is unknown at compile time.

• SQL that is not supported as static SQL

That is, any SQL construct not included in Description of Static SQL.

If you do not need dynamic SQL, use static SQL, which has the following advantages:

Successful compilation verifies that static SQL statements reference valid database
objects and that the necessary privileges are in place to access those
objects.Successful compilation creates schema object dependencies.

Using Native Dynamic SQL:-

Native dynamic SQL processes most dynamic SQL statements by means of the
EXECUTE IMMEDIATE statement.If the dynamic SQL statement is a SELECT statement
that returns multiple rows, native dynamic SQL gives you the following choices:

 Use the EXECUTE IMMEDIATE statement with the BULK COLLECT INTO
clause.

 Use the OPEN-FOR, FETCH, and CLOSE statements.

154
Using the EXECUTE IMMEDIATE Statement:-

The EXECUTE IMMEDIATE statement is the means by which native dynamic SQL
processes most dynamic SQL statements.

If the dynamic SQL statement is self-contained (that is, if it has no placeholders for
bind arguments and the only result that it can possibly return is an error), then the
EXECUTE IMMEDIATE statement needs no clauses.

If the dynamic SQL statement includes placeholders for bind arguments, each
placeholder must have a corresponding bind argument in the appropriate clause of the
EXECUTE IMMEDIATE statement

If the dynamic SQL statement is a SELECT statement that can return at most one row,
put out-bind arguments (defines) in the INTO clause and in-bind arguments in the
USING clause.

If the dynamic SQL statement is a SELECT statement that can return multiple rows, put
out-bind arguments (defines) in the BULK COLLECT INTO clause and in-bind arguments
in the USING clause.

If the dynamic SQL statement is a DML statement other than SELECT, without a
RETURNING INTO clause, put all bind arguments in the USING clause.

If the dynamic SQL statement is a DML statement with a RETURNING INTO clause, put
in-bind arguments in the USING clause and out-bind arguments in the RETURNING
INTO clause.

If the dynamic SQL statement is an anonymous PL/SQL block or a CALL statement, put
all bind arguments in the USING clause.

Invoking a Subprogram from a Dynamic PL/SQL Block:-

CREATE PROCEDURE create_dept ( deptid IN OUT NUMBER,

dname IN VARCHAR2,

mgrid IN NUMBER,

locid IN NUMBER

) AS

BEGIN

deptid := departments_seq.NEXTVAL;

INSERT INTO departments VALUES (deptid, dname, mgrid, locid);

END;

155
DECLARE

plsql_block VARCHAR2(500);

new_deptid NUMBER(4);

new_dname VARCHAR2(30) := 'Advertising';

new_mgrid NUMBER(6) := 200;

new_locid NUMBER(4) := 1700;

BEGIN

-- Dynamic PL/SQL block invokes subprogram:

plsql_block := 'BEGIN create_dept(:a, :b, :c, :d); END;';

/* Specify bind arguments in USING clause.

Specify mode for first parameter.

Modes of other parameters are correct by default. */

EXECUTE IMMEDIATE plsql_block

USING IN OUT new_deptid, new_dname, new_mgrid, new_locid;

END;

Unsupported Data Type in Native Dynamic SQL:-

DECLARE
FUNCTION f (x INTEGER)
RETURN BOOLEAN
AS
BEGIN
...
END f;
dyn_stmt VARCHAR2(200);
b1 BOOLEAN;
BEGIN
dyn_stmt := 'BEGIN :b := f(5); END;';
-- Fails because SQL does not support BOOLEAN data type:
EXECUTE IMMEDIATE dyn_stmt USING OUT b1;
END;
Using the OPEN-FOR, FETCH, and CLOSE Statements:-

If the dynamic SQL statement represents a SELECT statement that returns multiple
rows, you can process it with native dynamic SQL as follows:

156
Use an OPEN-FOR statement to associate a cursor variable with the dynamic SQL
statement. In the USING clause of the OPEN-FOR statement, specify a bind argument
for each placeholder in the dynamic SQL statement.
Use the FETCH statement to retrieve result set rows one at a time, several at a time,
or all at once. For syntax see FETCH Statement.
Use the CLOSE statement to close the cursor variable.For syntax see CLOSE
Statement.
Example Native Dynamic SQL with OPEN-FOR, FETCH, and CLOSE Statements :
DECLARE
TYPE EmpCurTyp IS REF CURSOR;
v_emp_cursor EmpCurTyp;
emp_record employees%ROWTYPE;
v_stmt_str VARCHAR2(200);
v_e_job employees.job%TYPE;
BEGIN
-- Dynamic SQL statement with placeholder:
v_stmt_str := 'SELECT * FROM employees WHERE job_id = :j';
-- Open cursor & specify bind argument in USING clause:
OPEN v_emp_cursor FOR v_stmt_str USING 'MANAGER';
-- Fetch rows from result set one at a time:
LOOP
FETCH v_emp_cursor INTO emp_record;
EXIT WHEN v_emp_cursor%NOTFOUND;
END LOOP;
-- Close cursor:
CLOSE v_emp_cursor;
END;
DBMS_SQL package :-

The DBMS_SQL package provides an interface to use dynamic SQL to parse any data
manipulation language (DML) or data definition language (DDL) statement using
PL/SQL. For example, you can enter a DROP TABLE statement from within a stored
procedure by using the PARSE procedure supplied with the DBMS_SQL package.
Subprograms of DBMS_SQL package :-
OPEN_CURSOR:-
To process a SQL statement, you must have an open cursor. When you call the
OPEN_CURSOR Function , you receive a cursor ID number for the data structure
representing a valid cursor maintained by Oracle.
Syntax:-
DBMS_SQL.OPEN_CURSOR RETURN INTEGER;
PARSE :-

157
Every SQL statement must be parsed by calling the PARSE Procedure. Parsing the
statement checks the statement's syntax and associates it with the cursor in your
program. You can parse any DML or DDL statement. DDL statements are run on the
parse, which performs the implicit commit.
Syntax:-
DBMS_SQL.PARSE ( c IN INTEGER,
statement IN VARCHAR2,
language_flag IN INTEGER);
BIND_VARIABLE or BIND_ARRAY:-
Many DML statements require that data in your program be input to Oracle. When you
define a SQL statement that contains input data to be supplied at runtime, you must
use placeholders in the SQL statement to mark where data must be supplied.
EXECUTE:-
Call the EXECUTE function to run your SQL statement.
Syntax:-
DBMS_SQL.EXECUTE ( c IN INTEGER) RETURN INTEGER;
CLOSE_CURSOR:-
When you no longer need a cursor for a session, close the cursor by calling
CLOSE_CURSOR.
Syntax:-
DBMS_SQL.CLOSE_CURSOR ( c IN OUT INTEGER);

The below procedure deletes all of the employees from the EMP table whose salaries
are greater than the salary that you specify when you run procedure
Example 1:-
CREATE OR REPLACE PROCEDURE delete_emp(salary IN NUMBER) AS
cursor_name INTEGER;
rows_processed INTEGER;
BEGIN
cursor_name := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cursor_name, 'DELETE FROM emp WHERE sal > :x',
DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE(cursor_name, ':x', salary);
rows_processed := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_SQL.CLOSE_CURSOR(cursor_name);
END;
After creating above procedure , it can be invoked as follows

158
SQL>EXECUTE delete_emp(2000);

After executing above procedure , it deletes all employee records earning more than
2000.

Example 2

The following sample procedure is passed a SQL statement, which it then parses and
runs:
CREATE OR REPLACE PROCEDURE exec_DDL(STRING IN varchar2) AS
cursor_name INTEGER;
ret INTEGER;
BEGIN
cursor_name := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(cursor_name, string, DBMS_SQL.NATIVE);
ret := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
END;
For example, after creating this procedure, you could make the following call:

SQL>execute exec_DDL('create table acct(c1 integer)');

Example 3 :-

Count no of records in all the tables in current schema ?

DECLARE
s varchar2(1000);
c1 integer;
status integer;
rowcount integer;
totalrows number(10);
BEGIN
for r in (select table_name from user_tables
order by table_name)
LOOP
s := 'select count(*) from '||r.table_name;
c1 := dbms_sql.open_cursor;
dbms_sql.parse(c1,s,dbms_sql.native);
dbms_sql.define_column(c1,1,totalrows);
status := dbms_sql.execute(c1);
rowcount := dbms_sql.fetch_rows(c1);
dbms_sql.column_value(c1,1,totalrows);
dbms_output.put_line(r.table_name||' '||totalrows||' records');

159
dbms_sql.close_cursor(c1);
END LOOP;
END;
Working With LOBs :-
Starting from Oracle8. Oracle database capable of stroing images, videos, maps all
types of unstructured data. 
 The LOB data type allows holding and manipulating unstructured data such as texts,
graphic images, video sound files. The dbms_lob package was designed to
manipulate LOB data types.  Oracle provides the dbms_lob package which is used to
access and manipulate LOB values in both internal or external storage locations.
 With this package dbms_lob, it is possible to read and modify given BLOB,
CLOB and NLOBtypes as well as effecting operations of reading in BFILEs.  The types
of data used for package dbms_lob include:
  BLOB
 RAW
 CLOB
 VARCHAR2
 INTEGER
 BFILE
 
LOBs are two types :-
   Internal LOBs (inline):  They are the LOBs stored inside of the database in a
way that optimizes performance and supplies an efficient access environment, taking
advantage of Oracle Database security and reliability features. These can be
persistent or temporary types.

  External LOBS (BFILE):  LOBs stored in an external location, as in an operating


system file.  The database itself holds an internal reference to the external file(s),
enabling access to each external files data via SQL (data type BFILE).
 
System-defined routines available in the DBMS_LOB package:-
Routine Name Description
APPEND procedure Appends one large object to another.
COMPARE function Compares two large objects.
COPY procedure Copies one large object to another.
ERASE procedure Erases a large object.
GETLENGTH function Gets the length of the large object.
INSTR function Gets the position
READ procedure Reads a large object.
SUBSTR function Gets part of a large object.
TRIM procedure Trims a large object to the specified length.
WRITE procedure Writes data to a large object.
The following lists the public variables available in the package.

160
Public variables Data type Value
lob_readonly INTEGER 0
lob_readwrite INTEGER 1
APPEND procedures
The APPEND procedures provide the capability to append one large object to
another.
COMPARE function
The COMPARE function performs an exact byte-by-byte comparison of two large
objects for a given length at given offsets.
COPY procedures
The COPY procedures provide the capability to copy one large object to another.
ERASE procedures
The ERASE procedures provide the capability to erase a portion of a large object.
GETLENGTH function
The GETLENGTH function returns the length of a large object.
INSTR function
The INSTR function returns the location of the nth occurrence of a specified
pattern within a large object.
READ procedures
The READ procedures provide the capability to read a portion of a large object
into a buffer.
SUBSTR function
The SUBSTR function provides the capability to return a portion of a large object.
TRIM procedures
The TRIM procedures provide the capability to truncate a large object to the
specified length.
WRITE procedures
The WRITE procedures provide the capability to write data into a large object.
Example :-
CREATE TABLE book (
id NUMBER (10) PRIMARY KEY,
isbn CHAR(10 CHAR),
description CLOB,
nls_description NCLOB,
misc BLOB,
chapter_title VARCHAR2(30 CHAR),
bfile_description BFILE);
SQL>INSERT INTO BOOK VALUES (id,isbn,description,nls_description,misc,
bfile_description)VALUES (1,'3', EMPTY_CLOB(),
EMPTY_CLOB(),EMPTY_BLOB(),

161
BFILENAME('book_LOC', 'b.pdf'));
1 row created.
DECLARE
v_dest_blob BLOB;
v_dest_clob CLOB;
v_source_locator1 BFILE := BFILENAME('book_LOC', 'bfile_example.pdf');
v_source_locator2 BFILE := BFILENAME('book_LOC', 'bfile_example.txt');
BEGIN
UPDATE book SET description = EMPTY_CLOB(),misc = EMPTY_BLOB();
SELECT description, misc INTO v_dest_clob, v_dest_blob FROM book
WHERE id = 1 FOR UPDATE;
DBMS_LOB.OPEN(v_source_locator1, DBMS_LOB.LOB_READONLY);
DBMS_LOB.OPEN(v_source_locator2, DBMS_LOB.LOB_READONLY);
DBMS_LOB.OPEN(v_dest_blob, DBMS_LOB.LOB_READWRITE);
DBMS_LOB.OPEN(v_dest_clob, DBMS_LOB.LOB_READWRITE);
DBMS_OUTPUT.PUT_LINE('Length of the BLOB file is: '||
DBMS_LOB.GETLENGTH(v_source_locator1));
DBMS_OUTPUT.PUT_LINE('Length of the CLOB file is: '||
DBMS_LOB.GETLENGTH(v_source_locator2));
DBMS_OUTPUT.PUT_LINE('Size of BLOB pre-load: '||
DBMS_LOB.GETLENGTH(v_dest_blob));
DBMS_OUTPUT.PUT_LINE('Size of CLOB pre-load: '||
DBMS_LOB.GETLENGTH(v_dest_clob));
DBMS_LOB.CLOSE(v_source_locator1);
DBMS_LOB.CLOSE(v_source_locator2);
DBMS_LOB.CLOSE(v_dest_blob);
DBMS_LOB.CLOSE(v_dest_clob);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
DBMS_LOB.CLOSE(v_source_locator1);
DBMS_LOB.CLOSE(v_source_locator2);
DBMS_LOB.CLOSE(v_dest_blob);
DBMS_LOB.CLOSE(v_dest_clob);

END;
/
Copying Object from BFILE to BLOB :-
create or replace procedure
update_cust_photo(d number,f varchar2)

162
is
src bfile;
tgt blob;
x number;
begin
select cphoto into tgt
from cust
where cid=d for update ;
src := bfilename('D55',f);
dbms_lob.open(src,dbms_lob.lob_readonly);
x := dbms_lob.getlength(src);
dbms_lob.loadfromfile(tgt,src,x);
update cust set cphoto = tgt
where cid=d;
commit;
dbms_lob.close(src); end;
Overview of Wrapping:-
Wrapping is the process of hiding PL/SQL source code. Wrapping helps to protect
your source code from business competitors and others who might misuse it.
You can wrap PL/SQL source code with either the wrap utility or DBMS_DDL
subprograms. The wrap utility wraps a single source file, such as a SQL*Plus
script. The DBMS_DDL subprograms wrap a single dynamically generated
PL/SQL unit, such as a single CREATE PROCEDURE statement.
Wrapped source files can be moved, backed up, and processed by SQL*Plus and
the Import and Export utilities, but they are not visible through the static data
dictionary views *_SOURCE.
Guidelines for Wrapping:-
 Wrap only the body of a package or object type, not the specification.
This allows other developers to see the information they must use the package
or type, but prevents them from seeing its implementation.
 Wrap code only after you have finished editing it.
You cannot edit PL/SQL source code inside wrapped files. Either wrap your code
after it is ready to ship to users or include the wrapping operation as part of your
build environment. To change wrapped PL/SQL code, edit the original source file
and then wrap it again.
 Before distributing a wrapped file, view it in a text editor to be sure that
all important parts are wrapped.
Limitations of Wrapping:-
 Wrapping is not a secure method for hiding passwords or table names.
 Wrapping a PL/SQL unit prevents most users from examining the source
code

163
 Wrapping does not hide the source code for triggers.
 To hide the workings of a trigger, write a one-line trigger that invokes a
wrapped subprogram.
 Wrapping does not detect syntax or semantic errors.
Wrapping PL/SQL Code with wrap Utility:-
The wrap utility processes an input SQL file and wraps only the PL/SQL units in
the file, such as a package specification, package body, function, procedure,
type specification, or type body. It does not wrap PL/SQL content in anonymous
blocks or triggers or non-PL/SQL code.
To run the wrap utility, enter the wrap command at your operating system
prompt using the following syntax
wrap iname = inputfile
input file is the name of a file containing SQL statements, that you typically run
using SQL*Plus. If you omit the file extension, an extension of .sql is assumed.
For example, the following commands are equivalent:
wrap iname=/mydir/myfile
wrap iname=/mydir/myfile.sql
You can also specify a different file extension:
wrap iname=/mydir/myfile.src
output_file is the name of the wrapped file that is created. The defaults to that
of the input file and its extension default is .plb. For example, the following
commands are equivalent:
wrap iname=/mydir/myfile
wrap iname=/mydir/myfile.sql oname=/mydir/myfile.plb
You can use the option oname to specify a different file name and extension:
wrap iname=/mydir/myfile oname=/yourdir/yourfile.out
Input and Output Files for the PL/SQL wrap Utility:-
The input file can contain any combination of SQL statements. Most statements
are passed through unchanged. CREATE statements that define subprograms,
packages, or object types are wrapped; their bodies are replaced by a scrambled
form that the PL/SQL compiler understands.
The following CREATE statements are wrapped:-
CREATE [OR REPLACE] FUNCTION function_name
CREATE [OR REPLACE] PROCEDURE procedure_name
CREATE [OR REPLACE] PACKAGE package_name
CREATE [OR REPLACE] PACKAGE BODY package_name
CREATE [OR REPLACE] TYPE type_name AS OBJECT
CREATE [OR REPLACE] TYPE type_name UNDER type_name
CREATE [OR REPLACE] TYPE BODY type_name
The CREATE [OR REPLACE] TRIGGER statement, and [DECLARE] BEGIN-END
anonymous blocks, are not wrapped. All other SQL statements are passed
unchanged to the output file.

164
All comment lines in the unit being wrapped are deleted, except for those in a
CREATE OR REPLACE header and C-style comments (delimited by /* */).
The output file is a text file, which you can run as a script in SQL*Plus to set up
your PL/SQL subprograms and packages. Run a wrapped file as follows:
SQL> @wrapped_file_name.plb;
Running the wrap Utility
For example, assume that the wrap_test.sql file contains the following:
CREATE PROCEDURE wraptest IS
TYPE emp_tab IS TABLE OF employees%ROWTYPE INDEX BY PLS_INTEGER;
all_emps emp_tab;
BEGIN
SELECT * BULK COLLECT INTO all_emps FROM employees;
FOR i IN 1..10 LOOP
DBMS_OUTPUT.PUT_LINE('Emp Id: ' || all_emps(i).employee_id);
END LOOP;
END;
/
To wrap the file, run the following from the operating system prompt:
wrap iname=wrap_test.sql
The output of the wrap utility is similar to the following:
PL/SQL Wrapper: Release 10.2.0.0.0 on Tue Apr 26 16:47:39 2005
Copyright (c) 1993, 2005, Oracle. All rights reserved.
Processing wrap_test.sql to wrap_test.plb
If you view the contents of the wrap_test.plb text file, the first line is CREATE
PROCEDURE wraptest wrapped and the rest of the file contents is hidden.
You can run wrap_test.plb in SQL*Plus to execute the SQL statements in the
file:
SQL> @wrap_test.plb
After the wrap_test.plb is run, you can execute the procedure that was created:
SQL> CALL wraptest();
Wrapping PL/QL Code with DBMS_DDL Subprograms:-
The DBMS_DDL package contains procedures for wrapping a single PL/SQL unit,
such as a package specification, package body, function, procedure, type
specification, or type body. These overloaded subprograms provide a mechanism
for wrapping dynamically generated PL/SQL units that are created in a database.
The DBMS_DDL package contains the WRAP functions and the
CREATE_WRAPPED procedures. The CREATE_WRAPPED both wraps the text and
creates the PL/SQL unit. When invoking the wrap procedures, use the fully
qualified package name, SYS.DBMS_DDL, to avoid any naming conflicts and the
possibility that someone might create a local package called DBMS_DDL or
define the DBMS_DDL public synonym. The input CREATE OR REPLACE

165
statement executes with the privileges of the user who invokes
DBMS_DDL.WRAP or DBMS_DDL.CREATE_WRAPPED.
Example Using DBMS_DDL.CREATE_WRAPPED Procedure to Wrap a
Package:-
DECLARE
package_text VARCHAR2(32767); -- text for creating package spec & body
FUNCTION generate_spec (pkgname VARCHAR2) RETURN VARCHAR2 AS
BEGIN
RETURN 'CREATE PACKAGE ' || pkgname || ' AS
PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER);
PROCEDURE fire_employee (emp_id NUMBER);
END ' || pkgname || ';';
END generate_spec;
FUNCTION generate_body (pkgname VARCHAR2) RETURN VARCHAR2 AS
BEGIN
RETURN 'CREATE PACKAGE BODY ' || pkgname || ' AS
PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER) IS
BEGIN
UPDATE employees
SET salary = salary + amount WHERE employee_id = emp_id;
END raise_salary;
PROCEDURE fire_employee (emp_id NUMBER) IS
BEGIN
DELETE FROM employees WHERE employee_id = emp_id;
END fire_employee;
END ' || pkgname || ';';
END generate_body;
BEGIN
-- Generate package spec
package_text := generate_spec('emp_actions')
-- Create wrapped package spec
DBMS_DDL.CREATE_WRAPPED(package_text);
-- Generate package body
package_text := generate_body('emp_actions');
-- Create wrapped package body
DBMS_DDL.CREATE_WRAPPED(package_text);
END;
/
-- Invoke procedure from wrapped package

166
CALL emp_actions.raise_salary(120, 100);
When you check the static data dictionary views *_SOURCE, the source is
wrapped, or hidden, so that others cannot view the code details. For example:
SELECT text FROM USER_SOURCE WHERE name = 'EMP_ACTIONS';
The resulting output is similar to the following:
TEXT
--------------------------------------------------------------------
PACKAGE emp_actions WRAPPED
a000000
1f
abcd

167

You might also like