You are on page 1of 196

ORACLE 9i

PL/SQL

1
2
Table of Contents

Topic No. Topic Name Page No.


1 Programming Basics 3
2 PLSQL Records 25
3 Cursors 28
4 PLSQL Tables 43
5 Exceptions 49
6 Procedures 58
7 Functions 73
8 Packages 82
9 Triggers 108
10 Dynamic SQL 129
11 DBMS_DDL Package 136
12 DBMS_JOB Package 138
13 UTL_File Package 140
14 BFile and LOBS 143
15 Managing Dependencies 150
16 Ref Cursor 156
17 Types 158
18 VArray 166
19 Nested Table 172
Assignments
1 Programming Basics 178
2 Cursors 181
3 PLSQL Tables 182
4 Exception Handling 183
5 Procedures 185
6 Functions 186
7 Packages 188
8 Triggers 190
9 Dynamic SQL 191
10 Varray 192
11 Nested Table 193

3
Topic No. 1 Programming Basics with PL/SQL Index

PL/SQL features ---


 PL/SQL is an extension of SQL
 It is an application development language containing procedural statements and
commands along with SQL commands
 It bridges the gap between database technology and procedural programming
languages
 It allows you to process data using flow control statements like iterative loops and
conditional branching
 Uses procedural techniques of control, looping and branching
 Supports SQL i.e. cursor operations, exceptions, functions and transactional
commands
 Variables and constants, robust error handling and functions
 Adds functionality to non-procedural tools such as SQL*Forms
 Developers using SQL*Forms can enter an entire PL/SQL block using a single
trigger

Structure of PL/SQL
 Standard PL/SQL code segment is called a Block
 A block consists of three parts or sections
– Declaration Part
– Executable Part
– Exception Handling Part

 Declaration Part
– optional part where variables are defined
 Executable Part
– mandatory part which consists of executable statements
 Exception Handling Part

4
– optional part which consists of code for handling errors (runtime)

Pl/SQL Files -
 PL/SQL programs can be written in any editor and saved as files with .sql
extension
 Can also use “ED” command in SQL*Plus to create a PL/SQL program file
 Use the “@ <filename>” command to execute a PL/SQL program file

o Variables --
 Used to store results of a query for later processing, or to calculate values to be
inserted into database tables
 Can be used anywhere in an expression, either in SQL or PL/SQL statements
 Must be declared before referencing it in other statements, including other
declarative statements
 Are declared by specifying the name along with the datatype
 Can be declared to be of any datatype native to Oracle
 Examples
oldfare NUMBER(5);
m_name VARCHAR(15);

(Note – Set Serveroutput On has to be given when a session starts for displaying the
output statements_)
declare
x number;
begin
x := 67;
dbms_output.put_line(x);
dbms_output.put_line('The value of x is '|| x);
end;
Declaring variable in declare block.
Assigning value in in begin block using := .
Output statement is dbms_output.put_line
Concatenation operator is ||
Command terminator is ; after end

Declaring and initializing variables together


declare
y number := 100;
begin
dbms_output.put_line('The value of y is '|| y);
end;
-------------------------------------------------------------------------------
Taking value from the user using &

5
declare
z number;
a varchar2(10);
begin
z := &z;
a := '&a';
dbms_output.put_line('Z is '|| z);
dbms_output.put_line('A is '|| a);
end;
-------------------------------------------------------------------------------

/*Cannot declare or initialize more than one variable simultaneously*/


declare
a number;
b number;
c number;
begin
a := 67; b := 90; c := 87;
dbms_output.put_line(a);
dbms_output.put_line(b);
end;

A constant number has to declared and initialized in the declare block only
using
CONSTANT keyword. Value cannot be changed

declare
r CONSTANT number :=100;
begin
/* r := r + 100; Not possible*/
dbms_output.put_line(r);
end;

/*Assigning value to variable from a column of a table using select into clause*/
declare
x number;
begin

Select sal Into x from emp


where ename = 'SMITH';
dbms_output.put_line('Salary of Smith is '|| x);
end;

6
/* Selecting ename,sal from emp
Use of more than one columns value with Into clause*/
declare
n varchar2(50);
s number;
begin
select ename, sal Into n, s
from emp
where ename = 'SMITH';
dbms_output.put_line(n);
dbms_output.put_line(s);
end;

% Type Attribute –
 Provides datatype of a variable or column
 Useful when declaring a variable that refers to a column in a database
– exact datatype of column need not be known
– if column definition changes, variable datatype changes accordingly at
runtime
 Example
oldfare fare.first_fare%TYPE;
newfare oldfare%TYPE;

declare
a emp.ename%type;
b emp.sal%type;
c emp.deptno%type;
/*Using %TYPE attribute for variable data type*/
begin
select ename,sal,deptno
into a,b,c
from emp
where ename = 'KING';
dbms_output.put_line(a ||'-'|| b ||'-' || c);
end;

%RowType Attribute –
 Useful when declaring a record variable having same structure as a row in a
table or view, or as a row fetched from a cursor
 Fields in the record have same names and datatypes as the columns in the
table/view
 Example
emp_rec employee%ROWTYPE;
 A specific field can be referenced using
emp_rec.emp_num;

7
declare
E emp%rowtype;
/*rowtype attribute holds the datatype of the columns of the
entire row*/
begin
select * INTO E
from emp
where ename = 'MARTIN';
dbms_output.put_line(E.sal);
dbms_output.put_line(E.ename);
dbms_output.put_line(e.deptno);
end;

Conditional Statements – IF
 The selection structure tests a condition, then executes one sequence of statements
instead of another, depending on the condition
 There are three forms of statements
– IF-THEN
– IF-THEN-ELSE
– IF-THEN-ELSIF
 Sequence of statements is executed only if the condition evaluates to TRUE
 If condition evaluates to FALSE or NULL, it does nothing
 In either case control passes to next statement after the IF-THEN structure
IF <condition> THEN
statements;
END IF;
 Sequence of statements in the ELSE clause is executed only if the condition
evaluates to FALSE or NULL
IF <condition> THEN
statements;
ELSE
statements;
END IF;
--------------------------------------------------------------------------------

8
declare
/*Simple if condition */
x number;
begin
x := &x;
if x >= 35 then
dbms_output.put_line('Passed');
else
dbms_output.put_line('Failed');
end if;
end;
-----------------------------------------------------
IF-THEN-ELSIF Structure

 This construct allows selection of action from several mutually exclusive


alternatives
 The IF statement can have any number of ELSIF clauses
 The final ELSE is optional
 Conditions are evaluated one by one from top to bottom

Syntax
IF <condition1> THEN
statements;
ELSIF <condition2> THEN
statements;
ELSIF <condition3> THEN
statements;
ELSE
statements;
END IF;
Example 1 –

9
Declare
y number;
/*Multiple ifs */
Begin
y := &y;

if y >= 70 then
dbms_output.put_line('Distinction');
elsif y >= 60 then
dbms_output.put_line('First class');
elsif y >= 50 then
dbms_output.put_line('Second class');
elsif y >= 35 then
dbms_output.put_line('Passed');
else
dbms_output.put_line('Failed');
end if;
end;

Example 2
create table adm
(Name varchar2(30),
Marks number(3),
College varchar2(30),
Fees number(5));

/*Use of multiple if's


Accept name and marks from user.
Depending upon marks entered the college and fees should be decided
and the record should be entered in the adm table.*/

Declare
n adm.name%type;
m adm.marks%type;
c adm.college%type;
f adm.fees%type;

Begin
n := '&n';
m := &m;

if m >= 95 then

10
c := 'COEP';
f := 10000;
elsif m >= 90 then
c := 'MIT';
f := 15000;
elsif m >= 85 then
c := 'VIT';
f := 22000;
elsif m >= 80 then
c := 'D Y Patil';
f := 27000;
elsif m >= 75 then
c := 'Pune Vidyarthi';
f := 33000;
else
dbms_output.put_line('Cannot get admission');
end if;

if c is not null and f is not null then


dbms_output.put_line('Your College is '|| c || ' and fees are ' || f);
Insert into adm
values(n,m,c,f);
commit;
end if;

end;

Nested Blocks
declare
x number;

begin
x := 80;
dbms_output.put_line(‘abc’);
declare
y number;
begin
y := 90;
dbms_output.put_line('Inner Block variable value ' || y);
end;

dbms_output.put_line('Outer Block variable value ' || x);


end;

11
Scope of variables

A variable declared in the outer block is accessible in the inner block. But a variable
declared in the inner block is accessible only in the inner block.

declare
outer number;
begin
outer := 80;
declare
inner number;
begin
inner := 90;
dbms_output.put_line('Inner Block variable value ' || inner);
dbms_output.put_line('Outer block variable is accessible in the inner
block’);
dbms_output.put_line('Outer block variable value ' || outer);
end;

dbms_output.put_line('Outer Block variable value ' || outer);


dbms_output.put_line('Inner Block variable value ' || inner);
end;
/

12
Labels

If the variables names of the outer and inner blocks are same then labels have to be
used within the inner block to avoid ambiguity.

<<outer_block>>
declare
x number;
begin
declare
x number := 100;
begin
dbms_output.put_line('Value of the inner block x is ' || x);
-- Giving value of x of the inner block to the outer block x
outer_block.x := x;
end;
x := x + 500;
dbms_output.put_line('Value of the outer block x is ' || x);
end;
/

13
LOOPS
 The ability to repeat or skip sections of a block can be achieved with the usage of
LOOP or GOTO statements
 There are three forms of the LOOP statement
– LOOP
– WHILE-LOOP
– FOR-LOOP
LOOP Statement
 LOOP repeats a sequence of statements
 Statements to be repeated are placed between keyword LOOP and END LOOP
 With each iteration of the loop, the sequence of statements is executed, then
control resumes at the top of the loop
LOOP
statements;
END LOOP;

EXIT Statement
 Used to complete a loop if further processing in a loop is undesirable or
impossible
 There are two forms of the EXIT statement
– EXIT
– EXIT-WHEN
 Forces a loop to complete unconditionally
 Must be placed inside a loop
LOOP
statements;
IF <condition> THEN
EXIT; -- exit loop immediately
END IF;
END LOOP;
-- control resumes here

Example of Loop –
/*To show 1 to 10 on screen*/

Declare
x number;
Begin
x := 1;
Loop
dbms_output.put_line(x);
x := x + 1;
exit when x > 10;
End Loop;
dbms_output.put_line(‘end’);

14
End;

create table five


(no number);
/*Inserting multiples of five in table five*/

Declare
x number;
Begin
x := 5;
Loop
Insert into five
values(x);
x := x + 5;
exit when x > 50;
End Loop;
End;

FOR LOOP
Advantages --
1) No need of declaring loop variable
2) No need of giving condition
3) No need of updation statement (increment or decrement )
4)Code becomes small and compact
Disadvantage --
Updation can be done by only one.

Syntax –
FOR <counter> IN [REVERSE]
lower_bound .. higher_bound LOOP
statements;
END LOOP

Example 1 of for loop


/*To show 1 to 10 on screen*/

begin
for x in 1..10
Loop
dbms_output.put_line(x);
End Loop;
end;

15
Example 2
/*Reverse for loop 10,9,8 … 1*/

Begin
for i in REVERSE 1 ..10
Loop
dbms_output.put_line(i);
End Loop;
end;

Example 3 –
Calculating compound interest for a principal of Rs.100 @10% for each year.
Values will be shown of the CI after each year.

create table CI_100


(year number(2),
total number(4));
----------------------------------
Declare
p number := 100;
tot number;
/*Calculation of compound interest.
Rs.100 is principal.
Rate of interest is 10%.
Period is 5 years.
*/
Begin
for y in 1..5
Loop
/* Tot variable is getting 10% more than p */
tot := p + p * 0.10;
Insert into CI_100
values(y,tot);
/*Since the next interest is based on the current interest
so the tot will be considered as p for the next year*/
p := tot;
End Loop;
end;

16
WHILE-LOOP Statement

 Associates a condition with a sequence of statements enclosed within LOOP-END


LOOP
 Condition evaluated before each iteration
 If condition evaluates to TRUE, sequence of statements is executed and control
resumes at the top of the loop
 If condition evaluates to FALSE or NULL, loop is bypassed and control passes to
next statement
 Number of iterations depends on the condition and is unknown until the loop
completes
WHILE <condition>
LOOP
statements;
END LOOP;

Example 1 of while loop to show 1 to 15

declare
x number;
Begin
x := 1;
while x <=15
Loop
dbms_output.put_line(x);
x := x + 1;
End Loop;
end;

Example 2 Forces a loop to complete unconditionally


declare
z number;
/*Using break after z reaches to 8*/
Begin
z := 1;
while z <=15
Loop
dbms_output.put_line(z);
z := z + 1;
exit when z = 8;
End Loop;
end;

17
While Loop v/s Basic Loop
While Loop
declare
x number;
Begin
x := 1;
while x > 15
Loop
dbms_output.put_line(x);
x := x + 1;
End Loop;
dbms_output.put_line('End of program');
end;
/

The loop will never get executed since the condition is wrong from the start of the
iteration.

Basic Loop
Declare
x number;
Begin
x := 1;
Loop
dbms_output.put_line(x);
exit when x = 1;
x := x + 1;
End Loop;
dbms_output.put_line('End of program');
End;
/

The loop gets executed at least once.

18
Nested Loops

create table discount_details


(quarter number(2),
month number(2),
discount varchar2(5));

Expected Output –

QUARTER MONTH DISCOUNT

1 1 12%
1 2 11%
1 3 10%
2 4 9%
2 5 8%
2 6 7%
3 7 6%
3 8 5%
3 9 4%
4 10 3%
4 11 2%
4 12 1%

12 rows selected.

19
declare
q number;
m number;
d number;
dis varchar2(10);
c number;

begin
q := 1;
m := 0;
d := 12;

loop
exit when q > 4;
c := 0;
loop
exit when c >= 3;
m := m + 1;
dis := d || '%';
insert into discount_details
values(q,m,dis);
d := d - 1;
c := c + 1;
end loop;
q := q + 1;
end loop;

end;

20
GOTO Statement
 Branches to a label unconditionally
 When executed, it changes the flow of control in a PL/SQL block
 Two parts needed to code a GOTO statement
– Define a label name
– Use the GOTO statement to pass control to the label
 Label name
– optionally used to name a PL/SQL block or statements within the block
– Defined using angular brackets (<< >>)
<<if_fare_label>>
IF condition THEN
statements;
END IF;
statements;
GOTO if_fare_label;
 Transfer of control using GOTO statement is allowed in the following places
– from a block to an executable statement
– branch from an exception handler into an enclosing block
 Transfer of control using GOTO statement is NOT allowed in the following
places
– from within one IF statement or loop clause to another
– from an enclosing block into a sub-block
– from an exception handler into the current block
– out of a subprogram
– to keywords

Examples of GOTO –
1)
create table prec
(name varchar2(20),
dept varchar2(20));

create table mahrec


(name varchar2(20),
dept varchar2(20),
city varchar2(30));

21
/*Pune records should go in both the tables prec and mahrec.
Any other city record should go in mahrec table only.*/

declare
n prec.name%type;
d prec.dept%type;
c mahrec.city%type;

begin
n := '&n';
d := '&d';
c := '&c';
if c = 'Pune' then
goto s1;
else
goto s2;
end if;
<<s1>>
Insert into prec
values(n,d);
<<s2>>
Insert into mahrec
values(n,d,c);
end;

Legal and Illegal Usage of GOTO


Ex 1
declare
x number;
begin
x := 90;
<<s1>>
dbms_output.put_line('S1');
if x = 90 then
dbms_output.put_line('Condition is true');
goto s1;
else
dbms_output.put_line('Condition is false');
end if;
end;
/

22
Ex 2

declare x number := 90;


begin
if x = 90 then
goto abc;
else
<<abc>>
dbms_output.put_line('Failed');
end if;
end;
/

Ex 3

declare
<<abc>>
x number := 9;
begin
goto abc;
end;
/

Ex 4 Cannot jump from the parent block into a child block.

Begin
dbms_output.put_line('a');
goto x1;
begin
<<x1>>
dbms_output.put_line('x1');
end;
dbms_output.put_line('b');
end;

23
Ex 5 Can jump from the child block into the parent block provided the label is after the
child block.

Begin
dbms_output.put_line('a');
begin
goto x1;
end;
dbms_output.put_line('b');
<<x1>>
dbms_output.put_line('x1');
end;
/

Ex 6 Cannot jump from the child block into the parent block if the label is above the child
block.

Begin
dbms_output.put_line('a');
<<x1>>
dbms_output.put_line('x1');
begin
goto x1;
end;
dbms_output.put_line('b');
end;
/

Ex 7 Can jump outside the loop.

declare
x number := 1;
begin
loop
dbms_output.put_line(x);
x := x + 1;
exit when x >= 10;
if x = 5 then
goto label1;
end if;
end loop;
<<label1>>
dbms_output.put_line('Came out of loop!');
end;

24
Ex 7 If the label is defined inside the loop then from outside the loop cannot jump inside
the loop.

declare
x number := 1;
begin
goto label1;
loop
dbms_output.put_line(x);
x := x + 1;
exit when x >= 10;
<<label1>>
dbms_output.put_line('Came inside the loop!');
end loop;
end;

Ex 8 Cannot jump from parent loop into the child loop;

begin
for a in 1 .. 5
loop
dbms_output.put_line(a);
if a = 3 then
goto label1;
end if;
for b in 1 .. 4
Loop
dbms_output.put_line(b);
<<label1>>
dbms_output.put_line('XYZ');
End Loop;
end loop;
end;
/

25
Topic No. 2 PL/SQL Records Index
 Objects of type RECORD are called PL/SQL records
 PL/SQL records have uniquely named fields, which can belong to different
datatypes
 Define a RECORD type
TYPE <typename> IS RECORD
(fieldname1 <fieldtype>
:
fieldnameN <fieldtype> ;
(%TYPE and %ROWTYPE can be used to specify <fieldtype>]

Example 1 of Record Type –

Declare
TYPE empdetails IS RECORD
(eno Emp.Empno%type,
name Emp.Ename%type,
s Emp.Sal%type);

VE empdetails;

Begin

Select empno,ename,sal Into VE


from Emp
where ename = 'SMITH';

dbms_output.put_line(VE.eno || ' - ' || VE.name || '-' ||VE.s);


End;

Example 2 of Record Type –

Declare
TYPE si IS RECORD
(p number,
n number,
r number := 4.5);
/* r variable of si type has been given value. */
VSI si;
x number;
Begin
VSI.p := 5000;
VSI.n := 6;
x := (VSI.p * VSI.n * VSI.r) / 100;

26
dbms_output.put_line(x);
End;

Using a record type in another record type.

Declare

type Address_details is record


(sector char(3),
colony varchar2(50),
bldg_name varchar2(25),
pincode number(7));

type personal_details is record


(name varchar2(60),
Addr Address_Details,
age number);

V personal_details;

Begin
V.name := 'John';
V.Addr.sector := 'S1';
V.Addr.colony := 'Model';
V.Addr.bldg_name := 'Hill View';
V.Addr.pincode := 6775;
dbms_output.put_line('The building name is ' || V.Addr.bldg_name);
dbms_output.put_line('The pincode is ' ||V.Addr.pincode);

End;

-------------------------------------

27
%Rowtype with Record
declare

type t1 is record
(ed emp%rowtype,
dd dept%rowtype);

VT1 t1;
Begin

select * into VT1.ed


from emp
where ename = 'KING';

select * into VT1.dd


from dept
where dname = 'ACCOUNTING';

dbms_output.put_line(VT1.ed.ename);
dbms_output.put_line(VT1.dd.dname);

end;
/

Assigning Values of one record to another.

declare
type t1 is record (eno Emp.Empno%type,
name Emp.Ename%type,
s Emp.Sal%type);
v1 t1;
v2 t1;
begin
v1.eno := 3;
v1.name := 'ds';
v1.s := 998;
v2 := v1;
dbms_output.put_line (v2.name);
end;

28
Topic No. 3 Cursors Index

 To process a SQL statement, PL/SQL opens a work area called a context area.
 PL/SQL uses this area to execute SQL statements and store processing
information
 A PL/SQL construct called ‘Cursor’ allows you to name a context area, access its
information and in some cases, control its processing

Explicit Cursors
Defined by the user to keep track of which row is being processed, when a query returns
multiple rows

Defining a Cursor
 A cursor is defined in the declarative part of the PL/SQL block by naming it and
associating it with a query
CURSOR <cursorname> IS
<SELECT statement>;
 Example
CURSOR emp_cur IS
SELECT empno, ename, job, sal
FROM emp;

 A Cursor can be manipulated using


– OPEN
– FETCH
– CLOSE
 Cursor must be declared before it can be referenced using the OPEN, CLOSE or
FETCH statements

The OPEN Statement


 Initializes or opens a cursor
 Cursor must be opened before any rows are returned by the query
OPEN <cursorname>
Example --
OPEN emp_cur;

The FETCH Statement


 Can be executed repeatedly until all rows have been retrieved
FETCH <cursorname> INTO var1, …, varN;
OR
FETCH <cursorname> INTO record_variable;
 Example
FETCH emp_cur INTO mrec;

The CLOSE Statement

29
 Closes the cursor and makes the active set undefined
CLOSE <cursorname>;
 Example
CLOSE emp_cur;

 Once a cursor is closed, it can be reopened by using the OPEN statement

Attributes of Explicit Cursors

 Every cursor has four attributes that can be used to access the cursor’s context
area
– %NOTFOUND
– %FOUND
– %ROWCOUNT
– %ISOPEN
 To use these attributes, simple append them to the name of the cursor
 %NOTFOUND
– evaluates to TRUE if last FETCH failed because no more rows were
available
– evaluates to FALSE if last FETCH returned a row
 %FOUND
– evaluates to TRUE if last FETCH returned a row
– evaluates to FALSE if last FETCH failed because no more rows were
available
 %ROWCOUNT
– returns the number of rows FETCHed from the active set so far
 %ISOPEN
– evaluates to TRUE if an explicit cursor is open
– evaluates to FALSE if an explicit cursor is closed

30
Cursor Mechanism:
1) Demo of First Fetch and then Exit

create table cur1


(a int);
insert into cur1 values(1);
commit;

declare
cursor cf is select * from cur1;
M cf%rowtype;
flag boolean;
begin
open cf;
loop
Fetch cf into M;
flag := cf%notfound;
if flag = true then
dbms_output.put_line('cf%notfound is true');
else
dbms_output.put_line('cf%notfound is false');
end if;
Exit when cf%notfound;
dbms_output.put_line(M.a);
end loop;
end;
/

2) Demo of First Exit and then Fetch

declare
cursor cf is select * from cur1;
M cf%rowtype;
flag boolean;
begin
open cf;
loop
Exit when cf%notfound;
flag := cf%notfound;
if flag = true then
dbms_output.put_line('cf%notfound is true');
else
dbms_output.put_line('cf%notfound is false');

31
end if;
Fetch cf into M;
dbms_output.put_line(M.a);
end loop;
end;
/

3) Demo of Row Count

declare
cursor cf is select * from emp;
M cf%rowtype;
x number;
begin
open cf;
x := cf%rowcount;
dbms_output.put_line(x);
fetch cf into M;
x := cf%rowcount;
dbms_output.put_line(x);
fetch cf into M;
x := cf%rowcount;
dbms_output.put_line(x);
fetch cf into M;
x := cf%rowcount;
dbms_output.put_line(x);
close cf;
open cf;
x := cf%rowcount;
dbms_output.put_line(x);
fetch cf into M;
x := cf%rowcount;
dbms_output.put_line(x);
end;

32
Examples of Cursor –
1) To transfer names and sal of employees from emp table where sal >= 2500 in table
try1
create table try1
(no number,
ename varchar2(50),
sal number);

Declare
Cursor cf is
select ename,sal
from emp
where sal >= 2500;
M cf%rowtype;
N number;
Begin
Open cf;
N := 0;
Loop
Fetch cf into M;
Exit when cf%notfound;
N := cf%rowcount;

Insert into try1


values(N,M.ename,M.sal);
End Loop;

Close cf;
End;

2) Use of %FOUND attribute

Accepting the job from user and finally showing how many such jobs are there.

Declare
Cursor cf is
select *
from emp
where job = '&J';

M cf%rowtype;

N number;

J Emp.Job%type;

33
Begin
Open cf;
N := 0;
Loop
Fetch cf into M;
Exit when cf%notfound;

If cf%found then
N := N + 1;
End if;
End Loop;
Close cf;
If N > 0 then
dbms_output.put_line('Total number of job ' || J || ' is '|| N);
else
dbms_output.put_line('No such job');
End If;
End;
----------------------------------------------------------------------

3) Use of IsOpen attribute


Declare
Cursor cf is
select ename, deptno
from emp
where deptno = 20;
M cf%rowtype;
/*The cursor is not opened before Loop. So using IsOpen attribute to open the
cursor if it is not open.*/
Begin
/* Cursor is not opened!!! */
Loop
If cf%IsOpen then
Fetch cf into M;
else
Open cf;
dbms_output.put_line('Cursor is now opened');
End if;
exit when cf%notfound;
dbms_output.put_line(M.ename ||'--' || M.deptno);
End Loop;
End;
--------------------------------------------------------------------------------------

34
4) Transferring the first five records from emp table into another table FirstFive

create table firstfive


as
select empno,ename,sal,deptno
from emp
where 1=2;

Declare
Cursor cf is
Select *
from emp;
M cf%rowtype;
N number;
Begin
Open cf;
N := 1;
while N <= 5
Loop
Fetch cf into M;
Insert into firstfive
values(M.empno,M.ename,M.sal,M.deptno);
N := N +1;
End Loop;
End;

5) Displaying the 3rd record entered in the table emp –

Declare
Cursor cf is
select *
from emp;
M cf%rowtype;
Begin
Open cf;
Loop
fetch cf into M;
if cf%rowcount = 3 then
dbms_output.put_line(M.empno||'-'||M.ename||'-'||M.sal||'-'||M.deptno);
EXIT;
end if;
exit when cf%notfound;
End Loop;
End;

35
6) To see the first person( or first record entered) who has got salary > 2800
Declare
Cursor cf is
select *
from emp
where sal > 2800;
M cf%rowtype;
Begin
Open cf;
Loop
fetch cf into M;
if cf%rowcount = 1 then
dbms_output.put_line(M.empno||'-'||M.ename||'-'||M.sal||'-'||M.deptno);
exit;
end if;

End Loop;
End;

Cursor FOR Loop


 Implicitly declares its loop index as a record of %ROWTYPE,
 Implicitly opens the cursor
 Repeatedly fetches rows of values from the active set into fields in the record
 Implicitly closes the cursor when all rows have been processed or the loop is
exited
 The statements in the loop construct are executed once for each row that satisfies
the query associated with the cursor name
 Cursor FOR loop is used to simplify coding
 No need of --
1)Open cursor
2)Fetch
3)Exit
4)Close cursor

7) To show records where salary is > 3000


Declare
Cursor cf is
select *
from emp
where sal >= 3000;
Begin
For mrec in cf
Loop
dbms_output.put_line(mrec.ename||' '||mrec.sal||' '||mrec.deptno);
End Loop;
End;

36
For Loops using sub queries
No need of declaring cursor.

A private cursor within an anonymous block can be created.

To show names of employees who have job MANAGER.

begin
for MREC in (select * from emp)
Loop
if MREC.job = 'MANAGER' then
dbms_output.put_line('Name is ' ||MREC.ename);
end if;
END LOOP;
end;

37
Parameterized Cursor
The same cursor can be reopened and closed with different active sets.

declare
cursor cf(pjob emp.job%type)
is
select empno,ename,job,sal
from emp
where job = pjob;
M cf%rowtype;
begin
open cf('ANALYST');
LOOP
FETCH CF INTO M;
EXIT WHEN CF%NOTFOUND;
dbms_output.put_line(M.ename);
end loop;
close cf;
open cf('CLERK');
LOOP
FETCH CF INTO M;
EXIT WHEN CF%NOTFOUND;
dbms_output.put_line(M.ename);
end loop;
close cf;
open cf(‘MANAGER’);
LOOP
FETCH CF INTO M;
EXIT WHEN CF%NOTFOUND;
dbms_output.put_line(M.ename);
end loop;
close cf;

END;

38
Cursor FOR UPDATE OF and CURRENT OF
 CURRENT OF clause is used in an UPDATE or DELETE statement to refer to
the current row of the cursor
 The cursor must be declared with the FOR UPDATE OF clause and must be open
and positioned on a row
 If the cursor is not open, CURRENT OF clause results in an error
8) Example of Cursor FOR UPDATE OF and CURRENT OF

create table esal


(empno number,
sal number);

insert into esal


values(1,16000);
insert into esal
values(2,14000);
insert into esal
values(3,8000);
insert into esal
values(4,6500);
insert into esal
values(5,9000);
insert into esal
values(6,11000);
insert into esal
values(7,5500);
insert into esal
values(8,3500);
insert into esal
values(9,2200);
insert into esal
values(10,7000);

39
Multiple updations depending on the salary clause in one pl/sql block –

Declare
Cursor cf is
select * from esal
For Update of sal;
M cf%rowtype;

Begin
Open cf;
Loop
Fetch cf into M;
exit when cf%notfound;

If M.Sal >= 16000 Then


M.Sal := 20000;

ElsIf M.Sal >= 14000 Then


M.Sal := 18200;

ElsIf M.Sal >= 12000 Then


M.Sal := 16700;

ElsIf M.Sal >= 10000 Then


M.Sal := 13500;

ElsIf M.Sal >= 8000 Then


M.Sal := 11000;

ElsIf M.Sal >= 6000 Then


M.Sal := 9500;

ElsIf M.Sal >= 4000 Then


M.Sal := 7500;

Else
M.Sal := 5000;

End If;

Update esal
set sal = M.Sal
Where Current Of cf;
End Loop;

40
End;

Implicit Cursors
• Automatically defined and opened, by Oracle, to process each
SQL statement
• most recently opened context area is referred to as a ‘SQL%’
cursor

Attributes of Implicit Cursors

 Although OPEN, CLOSE and FETCH statements cannot be used to manipulate


the SQL% cursor, the attributes can be used to access its context area
 Attributes evaluate to NULL, before the cursor is opened automatically
 The following four cursor attributes can be used to access the SQL%
cursor’s context area
 SQL%NOTFOUND
 SQL%FOUND
 SQL%ROWCOUNT
 SQL%ISOPEN
 SQL%NOTFOUND
 evaluates to TRUE if an INSERT, UPDATE or DELETE statement
affected no rows, else it evaluates to FALSE
 SQL%FOUND
 logical opposite of SQL%NOTFOUND
 evaluates to TRUE if an INSERT, UPDATE or DELETE affected one or
more rows, else it evaluates to FALSE

 SQL%ROWCOUNT
– returns the number of rows affected by an INSERT, UPDATE or DELETE
statement
 SQL%ISOPEN
– Oracle automatically closes an implicit cursor after executing its
associated SQL statement
– For an implicit cursor SQL%ISOPEN always evaluates to FALSE

9) Example of Implicit Cursors

Begin

Delete from emp


where ename = '&name';

If SQL%Found Then

41
dbms_output.put_line('Record found and it is deleted');
End If;

If SQL%NotFound Then
dbms_output.put_line('No record is present of the given name.');
End If;

End;

10) Implicit Cursor for rowcount

Declare
C number := 0;
Begin
Update Emp
set sal = sal + 500
where deptno = &deptno;

/*If no record is updated since the deptno supplied is wrong then giving
the customised error message.*/
If SQL%Rowcount = 0 then
dbms_output.put_line('No records are updated since the department number entered
is not in the table.');
End if;

/*To prevent sal to be updated where deptno is > 3 */


If SQL%RowCount > 3 then
Rollback;
dbms_output.put_line('Cannot update since there are more than 3 deptnos');
End If;

If SQL%RowCount Between 1 and 3 then


c := SQL%RowCount;
dbms_output.put_line(c || ' records updated.');
End If;
End;

42
43
Topic No.4 PL/SQL Tables Index
Features of PL/SQL tables are as follows –
1) It is a composite data type.
2) They are modeled as similar to database tables, but they are not permanent tables.
So they can be created and manipulated only in a PL SQL block.
3) They can have only one column but any data type
4) It will have a primary key which is compulsory for the reference of values
5) There is no name to the column and primary key
6) The data type of the primary key is BINARY_INTEGER.
BINARY_INTEGER is a special data type which can be given only to the
column of PL/SQL table for it’s indexing purpose to store and retrieve values.
Range of binary_integer is -2147483647 to + 2147483647
7) Size is unconstrained (Table size grows as the rows are added to the table).
8) Can visualize a Pl/SQL table as a single dimensional vertical array, which can
hold unlimited elements.
9) Suitable for storing and displaying the values of one column of a table given
by a cursor.

Example of PL SQL Table –

Each name from the emp table is given to the vname plsql table by using cursor.
Then those names from vname table are displayed .

Declare
Type nametable IS TABLE OF CHAR(10) INDEX BY BINARY_INTEGER;
/*Creating variable vname of nametable type.*/
vname nametable;
Cursor cf is select ename from emp;
i number;
vrows number;
/*i is for the loop and vrows is for displaying the total names from the vname
table*/
Begin
Open cf;
i := 1;
Loop
Fetch cf into vname(i); /*Transferring each ename into vname
table*/
Exit when cf%NotFound;

vrows := cf%rowcount;
i := i + 1;
End Loop;
Close cf;

44
/*Now retrieving the names from the vname plsql table using for loop.*/
For n in 1..vrows
Loop
dbms_output.put_line('Name is '||vname(n));
End Loop;
End;

Properties of a PL SQL table ---


 Exists
 Count
 First
 Last
 Next
 Prior
 Delete

declare
Type discount is TABLE OF number
INDEX By Binary_Integer;
d discount;
Begin
d(5) := 90;
d(2) := 50;
d(8) := 70;
d(11) := 67;
d(14) := 68;
d(1) := 1;
d(23) := 5;
d(23) := 51;
dbms_output.put_line('The value at 23 index number is ' || d(23));
dbms_output.put_line('The value at index number 6 is ' || d(6));

if d.EXISTS(6) Then
dbms_output.put_line(d(6));
else
dbms_output.put_line('There is no element in the sixth row');
end if;

dbms_output.put_line('The total number of elements in d are '|| d.count);

/*dbms_output.put_line('The first index number is ' || d.FIRST);

45
dbms_output.put_line('The last index number is ' || d.LAST);

dbms_output.put_line('The index number after 2 is ' || d.next(2));


dbms_output.put_line('The index number before 8 is ' || d.prior(8));

d.delete(5);
dbms_output.put_line('The total number of elements in d are '|| d.count);
d.delete(11,14);
dbms_output.put_line('The total number of elements in d are '|| d.count);
d.delete;
dbms_output.put_line('The total number of elements in d are '|| d.count); */

end;

Table made up of %Rowtype

Declare
Type nametable IS TABLE OF EMP%ROWTYPE INDEX BY
BINARY_INTEGER;
/*Creating variable vname of nametable type.*/
vname nametable;
Cursor cf is select * from emp;
i number;
vrows number;
/*i is for the loop and vrows is for displaying the total names from the vname
table*/
Begin
Open cf;
i := 1;
Loop
Fetch cf into vname(i); /*Transferring each ename into vname
table*/
Exit when cf%NotFound;
vrows := cf%rowcount;
i := i + 1;
End Loop;
Close cf;
/*Now retrieving the names from the vname plsql table using for loop.*/
For n in 1..vrows
Loop
dbms_output.put_line('Name is '||vname(n).ENAME);
End Loop;
End;
/

46
Table made of record type

declare
type r is record (a number, b number);
type t is table of r index by binary_integer;
v t;
v1 r;
begin
v1.a := 89;
v1.b := 9;
v(1) := v1;
dbms_output.put_line(v(1).a);
end;

or

declare
type r is record (a number, b number);
type t is table of r index by binary_integer;
v t;
begin
v(1).a := 8;
dbms_output.put_line(v(1).a);
end;
/

Bulk Binding
This article begins a discussion of how to work with collections. Previous
versions of Oracle had limitations for collection usage in regards to SQL
statement processing. Bulk Bind and Collect features were introduced to reduce
the SQL processing overhead by efficient use of collections in PL/SQL code.

The PL/SQL engine executes procedural statements and sends all SQL
statements present in the code to the SQL engine. The SQL engine will parse
and execute the query or DML statement and return the expected output back to
the PL/SQL engine. This switch between the two engines is called context
switching.

We mostly concentrate on the SQL statement to tune performance issues. It is


worth noting that excessive context switching can affect performance. This would
be substantially significant when we are carrying out SQL statements in a loop.
The features discussed below were introduced to reduce this overhead of SQL
processing. Introduced in Oracle 8i, these features are being improved on with
every new release.

47
Two PL/SQL features, Bulk Bind and Bulk collect help in improving performance
and utilizing resources effectively from within PL/SQL code. These features
reduce context switching, (i.e., multiple SQL statements being executed from the
code resulting in calls to SQL engine), and carry out the operation on the
concerned object at one go. Since the SQL statements are fired in a bundle,
frequent access to SQL engine is reduced.

In cases where the PL/SQL code is being executed on a different terminal than
the server itself, this would also result in optimal network usage rather than too
frequent network calls to access the SQL engine.

Bulk Collects (Reading data in bulk)

The bulk collect option instructs the SQL engine to bulk bind the output
collections before returning them to the PL/SQL engine. This allows us to load
data dynamically into collections at one shot for further processing. Bulk collect
can be used with SELECT INTO, FETCH INTO and RETURNING INTO
statements.

Syntax:

... bulk collect into collection...


For example, let us assume that we need to load all pending transactions into a
temporary table and process them one by one. As part of validation, there is a
need to refer to the data in the same table, from time to time, for each transaction
being processed. One possible method to write the code would be to load all of
the data in the temporary table to a collection type. This way, additional queries
on the table could be avoided (context switch) and the processing could be
carried out in PL/SQL itself. This idea is further improved on by the use of the
bulk collect option, as all data is loaded into PL/SQL at the same time.

Examples of Bulk

Bulk used with Select into clause

declare
type emp_details is table of emp.ename%type index by binary_integer;
V emp_details;
begin
select ename bulk collect into V
from emp;
for i in V.first .. V.last
loop
dbms_output.put_line(V(i));
end loop;
end;

48
Bulk used in Cursors
declare
cursor cf is select * from emp;
type emp_tab is table of emp%rowtype index by binary_integer;
V emp_tab;
begin
open cf;
fetch cf bulk collect into V;
for j in V.first .. V.last
loop
dbms_output.put_line(V(j).ename);
end loop;
end;

Limit Clause
To restrict the bulk collect to certain number of rows the limit clause
is to be used.

declare
cursor cf is select * from emp;
type emp_tab is table of emp%rowtype index by binary_integer;
V emp_tab;
first_4 int;
begin
open cf;
first_4 := 4;
fetch cf bulk collect into V limit first_4-- The first 4 rows are given
for j in V.first .. V.last
loop
dbms_output.put_line(V(j).ename);
end loop;
end;

Example of processing first 4 rows, after that processing the next set
of 4 rows.

declare
cursor cf is select * from emp;
type emp_tab is table of emp%rowtype index by binary_integer;
V emp_tab;
first_4 int;
next_4 int;
begin
open cf;
first_4 := 4;
fetch cf bulk collect into V limit first_4;
for j in V.first .. V.last
loop
dbms_output.put_line('First 4 names -- ' ||V(j).ename);
end loop;

49
next_4 := 4;
fetch cf bulk collect into V limit next_4;
for j in V.first .. V.last
loop
dbms_output.put_line('Next 4 names -- ' ||V(j).ename);
end loop;
end;
/

Note  PLSQL Tables are also known as Associative Arrays.

50
Topic No. 5 EXCEPTIONS Index
Introduction to Exceptions

 An error condition is called an Exception


 When an error occurs, an exception is raised i.e. normal execution stops and
control transfers to the exception handling part of the PL/SQL block or
subprogram
 To handle raised exceptions, separate routines called exception handlers are
written

 There are two types of exceptions


– Pre-defined exceptions (Internal Exceptions)
– User-defined exceptions
 You cannot declare an exception twice in the same block, but can declare the
same exception in two different blocks
 Exceptions declared in a block are local to that block and global to all its sub-
blocks
 Enclosing blocks cannot reference exceptions declared in a sub-block because
blocks can only reference local or global exceptions
Predefined Exceptions
 Are implicitly raised whenever a PL/SQL block violates an Oracle rule or exceeds
a system-dependent limit
 Every Oracle error has a number, but exceptions must be handled by name
 PL/SQL predefines some common Oracle errors as exceptions
 These predefined exceptions are declared globally by PL/SQL
 Some Pre-defined Exceptions
 CURSOR_ALREADY_OPEN
 NO_DATA_FOUND
 TOO_MANY_ROWS
 VALUE_ERROR
 ZERO_DIVIDE

 More than one exception can be handled in a single exception handler by


separating them with the keyword OR
EXCEPTION
WHEN NO_DATA_FOUND OR TOO_MANY_ROWS THEN
statements;
WHEN OTHERS THEN
statements;
END;

51
Examples of Exception handling –
1) NO_DATA_FOUND error (Variable is not having any value.)

declare
n emp.ename%type;
s emp.sal%type;
begin
select sal into s
from emp
where ename = '&n';
dbms_output.put_line('Salary is '|| s);

/* Exception
When NO_DATA_FOUND then
dbms_output.put_line('No record'); */
end;

2) TOO_MANY_ROWS error (Variable is having more than one value)

declare
s emp.sal%type;
begin
select sal into s
from emp;
dbms_output.put_line('The salary is '|| s );
Exception
When TOO_MANY_ROWS then
dbms_output.put_line('Variable can hold only one value at a time');
dbms_output.put_line('Please specify the name of person for getting the
salary');
end;

3) ZERO_DIVIDE error (A number divided by zero)


declare
x number;
y number;
z number;
begin
x := &x;
y := &y;
z := x/y;
dbms_output.put_line('The answer is ' || z);

Exception

52
When ZERO_DIVIDE then
dbms_output.put_line('Cannot divide by zero!!!');
end;

4) DUP_VAL_ON_INDEX error (When a duplicate value is entered in a column


having Unique constraint)
declare
e emp.empno%type;
begin
e := &e;
insert into emp (empno )
values(e);
dbms_output.put_line('Successful');
Exception
When DUP_VAL_ON_INDEX then
dbms_output.put_line('Value already exists');
end;

5) VALUE_ERROR (Error in conversion of string to number)

declare
n number;
begin
n := '&n';
dbms_output.put_line(n);
Exception
When VALUE_ERROR then
dbms_output.put_line('Please enter number only');
end;

6) OTHERS (If no error handler works then at least OTHERS will work)

declare
x number;
y number;
z number;
begin
x := &x;
y := &y;
z := x/y;
dbms_output.put_line('The answer is ' || z);

Exception

53
When too_many_rows then
dbms_output.put_line('More than one value');
When no_data_found then
dbms_output.put_line('No value');
/*When OTHERS then
dbms_output.put_line('Some run time error has occurred');
dbms_output.put_line('Please execute the program again with proper values.');
rollback;*/
end;

Pragma Exception

create table dept1(deptno number primary key, dname varchar2(10));


create table emp1(empno number, ename varchar2(10),
deptno number references dept1(deptno));
insert into dept1 values(10,'Acc');
insert into emp1 values(1,'abc',10);

PRAGMA  PRAGMA EXCEPTION_INIT tells the compiler to associate an


exception name with an Oracle error number. That allows you to refer to any
internal exception by name and to write a specific handler for it.

declare
referential_integrity EXCEPTION;
PRAGMA EXCEPTION_INIT( referential_integrity, -2292);
begin
Delete from dept1
where deptno = &deptno;
commit;
exception
when referential_integrity then
dbms_output.put_line('The record cannot be deleted, because related record found in
emp1 table');
end;

Oracle Error Number for the Constraints violations:

Primary Key and Unique is -00001


Foreign Key is -2291,
Not Null is -1400
Check is -2290

54
SQLCODE AND SQLERRM
SQLCODE  Returns the numeric value for the error code.
SQLERRM  Returns the message associated with the error number.

create table error_log(error_number number, error_message varchar2(255));

declare
s emp.sal%type;
v_error_code number;
v_error_message varchar2(255);
begin
select sal into s from emp;
exception
when others then
v_error_code := SQLCODE;
v_error_message := SQLERRM;
Insert into error_log values(v_error_code, v_error_message);
commit;
end;

Exception Handlers in nested block to continue after run time error comes

declare
loan_amt number;
no_of_months number;
installment_rate number;
roi number;
tot_amt number;
begin
loan_amt := &loan_amt;
no_of_months := & no_of_months;
begin
installment_rate := loan_amt / no_of_months;
exception
when zero_divide then
no_of_months := 3;
installment_rate := loan_amt / no_of_months;
end;
/* In any case the last 3 lines will get executed */

roi := installment_rate * 0.2; -- 20% roi


tot_amt := roi + loan_amt;
dbms_output.put_line('The total amount to be paid is '|| tot_amt);

55
end;

56
User-defined Exceptions

 User-defined exceptions need to be defined in the declarative part of a PL/SQL


block, subprogram or database trigger
 Declared by naming the exception and defining it as datatype EXCEPTION
 Example
DECLARE
past_due EXCEPTION;
zero_error EXCEPTION;

 Like variables, user-defined exceptions must be given names


 Unlike variables, user-defined exceptions cannot be assigned values and cannot be
used in SQL statements
 They need to be raised explicitly using the RAISE statement

 A block should RAISE an exception only when an error makes it impossible or


impractical to finish processing
 RAISE statement for a given expression can be coded anywhere within the scope
of that expression
IF mrec.ss_fare <= 0 THEN
RAISE zero_error;
END IF;

 An exception raised inside a handler immediately propagates to the enclosing


block, which is searched to find a handler for the newly raised exception
 From there on, the exception propagates normally
 To re-raise an exception place a RAISE statement in its local handler

57
Example of Exception variable using Raise key word
declare
p number;
n number := 6;
si number;
r number := 10.5;
EX exception;

Begin
p := &p;
if p < 100 then
raise EX;
else
si := (p * n * r) / 100;
dbms_output.put_line('The Simple Interest is '|| si);
end if;
Exception
When EX then
dbms_output.put_line('The principle amt should be greater than or equal to
100.');
end;
--------------------------------------------------------------------------------------

RAISE_application_error

This can be used to create user defined error message, which can be more
descriptive than named exceptions.

Syntax - :
Raise_application_error(error number,error message);
where error number is any parameter between -20,000 and -20,999.Error message
is text that is associated with this error. The message parameter must be less than 512
characters.

Example of Raise_application_error
declare
maths number;
Begin
maths := &maths;

if maths < 35 then


raise_application_error(-20001,'Failed');
else
dbms_output.put_line('Passed');
end if;

58
end;
---------------------------------------------------------------------------------------------------
Example of Raise_application_error and error handling together –

declare
x number;
begin
x := '&x';
if x < 0 then
raise_application_error(-20009,'ty');
end if;
exception
when value_error then
dbms_output.put_line('ff');
end;

59
Topic No. 6 PROCEDURES Index

Advantages of Subprograms
 Provide Extensibility
– PL/SQL language can be tailored to suit the needs of the application
 Promote reusability and maintainability
– Once validated, they can be used with confidence in any number of
applications
– Simplifies maintenance/enhancement, as subprogram is only affected if
definition changes
 Provide Modularity
– Program can be broken down into manageable, well-defined logical
modules
– Supports top-down design and stepwise refinement approach to problem
solving
 Aid in abstraction
– Allow mental separation from particulars
– Stubs allow programmers to defer definition of procedures/functions until
main program is tested and debugged

 Procedure performs specific action


 Stored in database and can be invoked or called by any anonymous block
 Can take parameters
 Datatype specifier in parameter declaration must be unconstrained

 Has two parts


– Specification
• begins with keyword PROCEDURE, ends with procedure name or
parameter list
– Body
• begins with keyword IS, ends with keyword END followed by
optional procedure name

CREATE [OR REPLACE] PROCEDURE <procedurename> [(parameter1, …


parameterN)] IS
[local declarations]
BEGIN
executable statements;
[EXCEPTION
exception handlers]

60
END [<procedurename>];

parameter stands for


variablename [IN|OUT|IN OUT] datatype [{:= | DEFAULT} value]
 When a procedure is created, Oracle automatically performs these steps
– Compiles the procedure
– Stores the compiled code
– Stores the procedure in the database
 The PL/SQL compiler used to compile the code
 If an error occurs, the procedure is created but it is invalid

 Enforce Data Security


– Can grant users access to a procedure that queries a table, but not
grant access to the table itself
 Improve Database Performance
– Less amount of information sent over network
– Compilation not required to execute code
– Procedure present in shared pool so retrieval from disk not required
 Memory Savings
– Only one copy of procedure needs to be loaded in memory for
execution by multiple users
 Increase in Development Productivity
– Can avoid redundant coding and increase productivity, by writing a
single procedure
 Integrity
– Procedures need to be tested only once, to guarantee that it returns
accurate results

Calling a Stored Procedure

 Can call a procedure in a PL/SQL statement


– Example
branch_sum(‘NYK’);
 Can call a procedure from SQL*Plus
– Example
SQL> EXECUTE branch_sum(‘NYK’);

61
Examples of Procedures –

1) --Procedure without parameters


create or replace procedure pr1
as
s emp.sal%type;
Begin
select sal into s
from emp
where ename = 'SMITH';
dbms_output.put_line(s);
end;

Parameter Modes for Procedures and Functions


 Used to define the behavior of formal parameters
 Can be used with any subprogram
 Three parameter modes
– IN (Default)
– OUT
– IN OUT
 IN
– allows values to be passed to the subprogram being called
– inside the subprogram it acts like a constant
– actual corresponding parameter can be a constant, literal, initialized
variable or expression
– can be initialized to default values

2) --Supplying parameters to a procedure which are by default of IN type

create or replace procedure pr2(En IN Emp.Empno%type, Name IN Emp.ename


%type, S Emp.Sal%type)
is
Begin
Insert into Emp(empno,ename,sal)
Values(En,Name,S);
dbms_output.put_line('One record inserted through procedure');
End;

3) Giving default values to the parameters


Due to default value given the parameter becomes optional also.
But if any other value is given then it takes it.

62
create or replace procedure pr3 (Eno emp.empno%type, N emp.ename%type, S emp.sal
%type, dno emp.deptno%type DEFAULT 10)
is
Begin
Insert into emp (empno,ename,sal,deptno)
values(Eno,N,S,dno);
dbms_output.put_line('Record inserted');
End;

-- While executing
--exec pr3 (1,'o',800) -----> (No deptno parameter given!!!)

4) --Cannot give size to the parameters


create or replace procedure pr4 (name char, marks number)
is
Begin
if marks >= 35 then
dbms_output.put_line('Passed');
else
dbms_output.put_line('Failed');
end if;
dbms_output.put_line(name);
End;

Named notations

create or replace procedure dept_rec(p_deptno in number, p_dname in dept.dname%type


default 'Accounts', p_loc dept.loc%type)
is
begin
Insert into dept
values(p_deptno, p_dname, p_loc);
dbms_output.put_line('Done');
end;

To exceute such a parameter we have to use the named notation

exec dept_rec(p_loc => 'Pune', p_deptno => 10);

Mutiple ways of passing the parameters --

63
1. Positional Method -- exec dept_rec(20,'Purchase','Mumbai');
2. Named Method –
a. exec dept_rec(p_loc => 'Bangalore',p_deptno => 30);
b. exec dept_rec(p_deptno =>20, p_dname => 'Inspection', p_loc =>
'Pune');
c. exec dept_rec(p_dname =>'Stores', p_loc => 'Mumbai', p_deptno =>
10);
3. Combination Method –
After positional there can be named parameters, but after named there cannot
be positional parameters.

a. exec dept_rec(10, p_loc =>'Mumbai', p_dname =>'Marketing');


b. exec dept_rec(p_deptno =>20, 'Pune','Research'); -- Wrong

OUT parameter
– allows values to be returned to the caller of a subprogram
– inside the subprogram it acts like an uninitialized variable
– actual corresponding parameter must be a variable; it cannot be a
constant or expression
– its value cannot be assigned to another variable or reassigned to itself

5)
create or replace procedure pr5(Name IN varchar2, Salary OUT number)
Is
Begin
Select sal into Salary
from emp
where ename = Name;
End;
--Steps for displaying the OUT parameter
--1) Compiling the procedure.
--2) Declare the bind variable on SQL prompt as variable payment number
-- Bind variables are of SQL* plus environment which are used to hold the return
--value given by the procedure or function.
--3)Now execute the proc -- exec pr5('SMITH', :payment)
--4)To display the value of payment -- print payment
--5)Bind variables are session specific.Their existence is removed as the session --ends.

64
Using local variable for out parameter when procedure is called inside another
block.

create or replace procedure p1(n in emp.ename%type, s out emp.sal%type)


is
begin
select sal into s
from emp
where ename = n;
end;

declare
x emp.sal%type;
begin
p1('SMITH',x);
dbms_output.put_line(x);
end;

6) IN OUT parameter
– allows initial values to be passed and returns updated values to the
caller
– inside the subprogram it acts like an initialized variable
– actual corresponding parameter must be a variable; it cannot be a
constant or expression
– can be assigned a value and its value can be assigned to another
variable

a) create or replace procedure pr6(x IN OUT number)


Is
Begin

x := (x * x);

End;

/*pr6 procedure cannot be executed independently on sql prompt.


It has to be called inside a plsql block. It actually gives the square value to the
variable of that plsql block.
In short IN OUT type of paramter makes a procedure similar to function, as the
function also returns the value to the calling environment.*/

65
b)

declare
a number;
Begin
a := &a;
pr6(a);

/*When a is given as a parameter , it's status is of IN OUT. So IN means the user


input value and OUT means the changes square figure due to the procedure pr6.
After the procedure is called with a as parameter then a value gets changed. At this
time a acts as a OUT parameter, since the procedure is giving the changed value to
a.*/

dbms_output.put_line(a);

End;

66
7) IN OUT example from with respect to database
a)
create or replace procedure salrise(salary IN OUT number) is
Begin
salary := salary + (salary * 0.20);
End;
/*Salrise procedure will increment the sal by 20% and give the value to the calling plsql
block.*/

b)
Declare
n emp.ename%type;
s emp.sal%type;
Begin
n := '&n';
select sal into s
from emp
where ename = n;
dbms_output.put_line('The old salary is ' || s);
/*Now calling the procdure Salrise and giving s as a IN parameter*/
Salrise(s);
/*After giving the salary as a parameter the salary value gets incremented by 20% */
dbms_output.put_line('The changed salary is '|| s);
/*updating the table*/
Update emp
set sal = s
where ename = n;
dbms_output.put_line('Salary of ' || n || ' is updated in the table.');
Exception
When NO_DATA_FOUND then
dbms_output.put_line('No such name');
end;

--------------------------------------------------------------------------------------------------

IN, OUT, IN OUT Modes Behavior with respect to the local variables

A procedure is having IN mode of parameter.

create or replace procedure p1(a in number)


is
begin
dbms_output.put_line('The value of a inside the procedure is '||a);
end p1;

67
declare x number;
begin
x := 3;
dbms_output.put_line('The value of x before sending to the procedure is ' || x);
p1(x);
dbms_output.put_line('The value of x after coming outside is '|| x);
end;

Output:

The IN mode value is accessible inside the procedure as well as the value still exists
after coming out of the procedure. Only the procedure cannot change the value.

A procedure is having OUT mode of parameter.

create or replace procedure p1(a out number)


is
begin
dbms_output.put_line('The value of a inside the procedure is '||a);
end p1;

declare x number;
begin
x := 3;
dbms_output.put_line('The value of x before sending to the procedure is ' || x);
p1(x);
dbms_output.put_line('The value of x after coming outside is '|| x);
end;

Output –

A procedure is having out mode of parameter.

68
If an initialized local variable is sent as a parameter then its original value is not
accessible inside the procedure.
Also after coming outside the procedure the original value is lost.

A procedure is having IN OUT mode of parameter.

create or replace procedure p1(a in out number)


is
begin
dbms_output.put_line('The value of a inside the procedure is '||a);
a := a + 7; -- Value can get changed
end p1;

declare x number;
begin
x := 3;
dbms_output.put_line('The value of x before sending to the procedure is ' || x);
p1(x);
dbms_output.put_line('The value of x after coming outside is '|| x);
end;

A procedure is having in out mode of parameter.


If an initialized local variable is sent as a parameter then its original value is
accessible inside the procedure. (Also it can get changed by the procedure)
After coming outside the procedure the original or changed value is retained by that
variable.

69
Declaring Sub procedures

Create table log_table(username varchar2(30), log_date date);

Create or replace procedure delete_emp(p_ename emp.ename%type)


Is
/*Creating a sub procedure */
Procedure log_emp
is
Begin
Insert into log_table
Values(user, SYSDATE);
End;

Begin

Delete from emp


where ename = p_ename;

log_emp;

End;

70
Handled or Unhandled Exceptions for a procedure getting called in another.

create or replace procedure innerproc(x number,y number)


is
begin
dbms_output.put_line(x/y);
exception
when zero_divide then
dbms_output.put_line('y cannot be negative');
end;

create or replace procedure outerproc


as
begin
dbms_output.put_line('AA');
/*Calling the innerproc precedure.*/
innerproc(4,0);
dbms_output.put_line('cc’);

exception
when others then
dbms_output.put_line('A Run Time Error has occurred.');
end;

If the innerproc procedure is not having any error handler then the error handler
outer procedure gets executed.

AUTHID CURRENT_USER

If a procedure reads data from table and performs some DML then AUTHID
CURRENT_USER is applicable. Due to this clause the owner of the procedure only can
execute this procedure and read or perform DML. Even if the owner has given execute
privilege of such a procedure to any other user, still the other user cannot execute the
procedure. So from data security point of view this clause is helpful.
The following scenario will explain this concept.

Case study ----

Log on as System/Manager (DBA Login)

create user a1 identified by a1;


grant resource, create session to a1;
-------------------------------------------------------
create user a2 identified by a2;

71
grant resource, create session to a2;
----------------------------------------------------------
Without AUTHID CURRENTUSER

conn a1/a1
create table t1(a number);
insert into t1 values(1);
commit;
-----------------------------------------------------------
create or replace procedure t1_data
is
x number;
begin
select a into x from t1;
dbms_output.put_line(x);
end;
----------------------------------------------
grant execute on t1_data to a2;
-------------------------------------------------
conn a2/a2
exec a1.t1_data --- Data is shown.

With AUTHID CURRENTUSER

conn a1/a1

create or replace procedure t1_data1


AUTHID CURRENT_USER
is
x number;
begin
select a into x from t1;
dbms_output.put_line(x);
end;
----------------------------------------
grant execute on t1_data1 to a2;

-------------------------------------------

conn a2/a2
exec a1.t1_data1 -- Data is not shown. Throws error.

72
Autonomous Transaction

Autonomous Transaction is a feature of oracle 8i which maintains the state


of its transactions and save it , to affect with the commit or rollback of the
surrounding transactions.
With PRAGMA AUTONOMOUS_TRANSACTION , the transaction state is
maintained independently . Commit/Rollback of nested transaction will
have no effect on the other transaction. It is advisable to increase the
value of TRANSACTIONS parameter in the INIT parameter file to allow for
the extra concurrent transaction .

Autonomous Transactions provide a new method of controlling transactions in stored


procedures. Autonomous Transactions allow you to create a new sub transaction that
may commit or rollback changes independent of the parent transaction.

Scenario One: Without Autonomous Transaction

Creating the test table t

CREATE TABLE t (test_value VARCHAR2(50));

Creating the procedure child_block

CREATE OR REPLACE PROCEDURE child_block IS


BEGIN
INSERT INTO t VALUES ('Child block insert');
COMMIT;
END child_block;

Creating the procedure parent_block

CREATE OR REPLACE PROCEDURE parent_block IS


BEGIN
INSERT INTO t VALUES ('Parent block insert');
child_block;
ROLLBACK;
END parent_block;

Run the parent procedure

exec parent_block;

Check the results

73
SELECT * FROM t;

TEST_VALUE
--------------------------------------------------
Parent block insert
Child block insert

The commit inside the child_block has committed the entire transaction
So there is no effect of rollback given by the parent_block

truncate table t;

Scenario Two: With Autonomous Transaction

CREATE OR REPLACE PROCEDURE child_block


IS
PRAGMA AUTONOMOUS_TRANSACTION;
BEGIN
INSERT INTO t VALUES ('Child block insert');
COMMIT;
END child_block;

Run the parent procedure

exec parent_block;

Check the results

SELECT * FROM t;

TEST_VALUE
--------------------------------------------------
Child block insert

Due to autonomous transaction the transaction of the child_block was only


committed and not the transaction of parent_block.

74
Topic No. 7 Functions Index

 Subprogram that returns a value


 Have a RETURN clause
 Stored in database and can be invoked or called by any anonymous block
 Generally take parameters
 Datatype specifier in parameter declaration must be unconstrained
 Has two parts
 Specification
 begins with keyword FUNCTION, ends with RETURN clause
 Body
 begins with keyword IS, ends with keyword END followed by
optional function name

Syntax –

CREATE [OR REPLACE] FUNCTION <functionname> [(argument1, … argumentN)]


RETURN datatype IS
[local declarations]
BEGIN
executable statements;
[EXCEPTION
exception handlers]
END [<functionname>];

argument stands for


variablename [IN|OUT|IN OUT] datatype [{:= | DEFAULT} value]

Format –

CREATE FUNCTION day_fn(mday NUMBER) RETURN CHAR IS


disp_day CHAR(15);
BEGIN
executable statements;
RETURN disp_day;
EXCEPTION
statements;
END day_fn;

Calling a Function

 Can call a function as a PL/SQL statement


– Example
chardays := day_fn(3);

75
 Can call a function as part of an expression
– Example
IF day_fn(3) = ‘TUESDAY’ THEN
statements;
END IF;

The RETURN Statement

 Immediately completes execution of subprogram and returns control to caller


 In procedures
– cannot contain an expression
– returns control to the caller before normal end of procedure is reached
 In functions
– must contain an expression, evaluated when it is executed

Examples of Functions –
1)
a)
create or replace function eo ( n number)
RETURN char
is
disp char(30);
Begin
If Mod(n,2) = 0 Then
disp := 'Number is even';
else
disp := 'Number is odd';
End If;
dbms_output.put_line(disp);

RETURN disp;

End;

Select eo(9) from dual;


OR

76
Declare
x number;
y char(30);
Begin
x := &x;

/*Calling the function eo, supplying the parameter x and giving the value to y*/

y := eo(x);

end;

2) Database example
a) create or replace function Remarks ( x number)
RETURN char is
disp char(30);
Begin
If x >= 70 Then
disp := 'Distinction';

Elsif x >= 60 Then


disp := 'First Class';

Elsif x >= 50 Then


disp := 'Second Class';

Elsif x >= 35 Then


disp := 'Passed';

Else
disp := 'Failed';

End If;

RETURN disp;

End;

c) Using this function to insert values for a column of a table


create table st
(name varchar2(10),

77
marks number,
result char(30));

Directly calling the function inside Insert statement


Insert into st
values(‘John’, 90, Remarks(90)); -- Directly calling the function in the values
clause

b) Calling a function in select statement

create or replace function raise_sal(s number)


return number is
begin
return s + 8000;
end;

Select ename,deptno,job,sal, raise_sal(sal)


From emp;

create or replace function f1(s number)


return number
is
begin
delete from emp;
return 0;
end;
Wrong usage of functions
select ename,sal,f1(sal) from emp;

Function having select statement in it’s body can get called in the select statement of the
same table or any different table.

create or replace function highest


return number
is
x number;
begin
select max(sal) into x from emp;
return x;
end;

select ename, sal, highest, highest - sal "Diff" from emp;

Applicable for displaying aggregate values alongwith non groupbale columns.

78
3) Returning more than one value using OUT parameter
a)
create or replace function getdetails(no number,name OUT varchar2)
RETURN varchar2 as
vloc varchar2(30);
begin
select dname,loc into name,vloc
from dept
where deptno = no;

RETURN vloc;
End;

b)
-- First declare two bind variables location and deptname
--SQL> variable deptname varchar2(100) (size is imp)
--SQL> variable location varchar2(100)
Begin
:location := getdetails(30, :deptname);
End;

-- To see both the values


-- print deptname location
-------------------------------------------------------------------------

Autonomous transaction used in a function.

We know that when a function is called inside a select statement then that function cannot
perform any DML.

But there can be a scenario where it is essential to keep a trace of all the rows that have
been touched by a query. That means in a select statement function will insert a new
record that will have details like table name, rowids, user name and sysdate.

Example 1 –

CREATE TABLE query_trace (


table_name VARCHAR2(30),
rowid_info ROWID,
queried_by VARCHAR2(30),
queried_at DATE
);

Function to perform the audit:

79
CREATE OR REPLACE FUNCTION traceit (
tab IN VARCHAR2,
rowid_in IN ROWID)
RETURN INTEGER
IS
BEGIN
INSERT INTO query_trace VALUES (tab, rowid_in, USER,
SYSDATE);
RETURN 0;
END;
/

select ename, traceit ('emp', rowid) from emp


*
ERROR at line 1:
ORA-14551: cannot perform a DML operation inside a query
ORA-06512: at "SCOTT.TRACEIT", line 7

CREATE OR REPLACE FUNCTION traceit (


tab IN VARCHAR2,
rowid_in IN ROWID)
RETURN INTEGER
IS
pragma autonomous_transaction;
BEGIN
INSERT INTO query_trace VALUES (tab, rowid_in, USER, SYSDATE);
commit; -- mandatory to commit
RETURN 0;
END;

select ename, traceit ('emp', rowid) from emp;

80
No error !!

SELECT table_name, rowid_info, queried_by,


TO_CHAR (queried_at, 'HH:MI:SS') “queried_at”
FROM query_trace;

81
Example 2 -

Some times a function is called in a select statement that changes the values of each row
and displays those. But we want to keep track of those changed values in a table.

create sequence seq1;

create table check_sal


(query_number number,
date_of_query date,
time_of_query varchar2(30),
changed_sal number);

create or replace function raise_sal(s number, growth number)


return number
is
v emp.sal%type;
pragma autonomous_transaction;
begin
v := s + growth;
insert into check_sal values(seq1.nextval, sysdate,to_char(sysdate,'hh:mi:ss'), v);
commit;
return v;
end;

select ename,sal, raise_sal(sal, 4000) "Raised" from emp;

82
select * from check_sal;

83
Topic No. 8 Packages Index
 Database objects that group logically related PL/SQL types, objects and
subprograms
 They cannot be called, passed parameters to or nested
 There are two parts
– Specification
– Body

Advantages of Packages
 Modularity
– allows encapsulation of related types, objects and subprograms in a named
PL/SQL module
– easy to understand with simple, clear and well defined interfaces
– helps in application development
 Easier Application Design
– when designing applications, only interface information in package
specifications initially required
– can code and compile specification without body
– stored subprograms referencing the package can be compiled as well
– need not define package bodies until ready to complete the application
 Information Hiding
– can specify which types, items, and subprograms are public or private
– definition of private subprograms is hidden so that only the package (not
the application) is affected if the definition changes
– simplifies maintenance and enhancement and protects integrity of the
package
 Better performance
– when a packaged subprogram is called for the first time, the whole
package is loaded into memory
– later calls to related subprograms in the package require no disk I/O
Package Specification
 Is an interface to the applications
 Declares the types, variables, constants, exceptions, cursors and subprograms
available for use
 Holds public declarations, visible to the application
 Can be thought of as an operational interface
 Scope of the declarations are local to the database schema and global to the
package
 Lists the package resources available to applications
 Created using CREATE PACKAGE command

84
Syntax for Package Specification –

CREATE [OR REPLACE] PACKAGE <packagename> AS


Global variables declaration;
Procedure specifications;
Function specifications;
Type Definitions;
Cursor Declarations
END [<packagename>];

Package Body

 Implements the package specification


 Fully defines cursors and subprograms
 Holds implementation details and private declarations, hidden from the
application
 Can be thought of as a ‘black body’
 Can be replaced, enhanced or replaced without changing the interface
 Can be changed without recompiling calling programs
 Scope of the declarations are local to the package body
 Declared types and objects are inaccessible except from within the package body
 Initialization part of a package is run only once, the first time the package is
referenced

Syntax for Package Body –

CREATE [OR REPLACE] PACKAGE BODY <packagename> AS


Private members (variables and procedure/functions/cursors/types)
Procedure Code;
Function Code;
Implementation of Types;
Use of Cursors;
Using Global variables in the members of the package.
END [<packagename>];

Referencing Package Objects

 Packaged objects and subprograms must be referenced using the dot notation
packagename.typename
packagename.objectname
packagename.subprogramname

E.g - DBMS_OUTPUT.PUT_LINE

85
Maintaining a Package

 Can drop a package using the DROP command


DROP PACKAGE <packagename>
 Example
DROP PACKAGE airlines;
To drop just one construct, remove it from the package and then recompile the
package

Examples of Packages -
1) Creating a package of 3 procedures -

Package Specification –

create or replace package pk1 is


procedure x(a number);
procedure y(b number);
procedure z(c number);
end PK1;

Package Body -
create or replace package body pk1
is
procedure x(a number)
is
Begin
dbms_output.put_line('Procedure p1');
End x;
procedure y(b number)
is
Begin
dbms_output.put_line('Procedure p2');
End y;
/*Suppose in the package body if all the procedures are not written then it will
give error.*/
/*procedure z(c number)
is
Begin
dbms_output.put_line('Procedure p3');
End z; */

86
End pk1;
-------------------------------
Using the Package pk1-
SQL > Execute PK1.X(4);

2) Use of global variable in a function and procedure -

Package Specification -

create or replace package pk2


as
g number;
function m(a number) return number;
procedure n;
end pk2;

Package Body -

create or replace package body pk2


as
function m(a number) return number
is
Begin
g := a;
return g;
End m;

procedure n
is
Begin
if g >= 100 then
dbms_output.put_line('Discount is 20%');
else
dbms_output.put_line('Discount is 10%');
end if;
end n;
End pk2;

Using the package in a PL/SQL block -

Declare
x number;
Begin
x := pk2.m(700);
pk2.n;

87
End;

88
3) Use of Type in a Procedure -

Package Specification -

create or replace package pk3 as


Type t1 is RECORD
(e1 Emp.Empno %Type,
e2 Emp.Ename%Type,
e3 Emp.Sal%Type);
Procedure p1;
end pk3;

Package Body -

create or replace package body pk3 as


procedure p1
is
v t1; /*Using the type of the package directly inside the procedure.*/
Begin
select empno,ename,sal into v
from emp
where ename = 'SMITH';
dbms_output.put_line(v.e1 || '-' || v.e2 || '-' || v.e3);
End;
End pk3;

4) Use of Cursor in Procedure -

Package Specification -

create or replace package pk4


as
cursor cf is select * from emp
where job = 'MANAGER';
m cf%rowtype;
procedure CP;
End pk4;

89
Package Body -

create or replace package body pk4 as


procedure CP
is
Begin
Open cf;
Loop
fetch cf into m;

/*Showing the first entry of manager*/


if cf%rowcount = 1 then
dbms_output.put_line(m.empno || '-' || m.ename || '-' || m.sal);
else
exit;
end if;
End Loop;
Close cf;
End CP;
End pk4;

5) Example of a body less package –


(Persistent state of variables)

create or replace package bodyless


is
x number := 200;
y number := 100;
/*Variables needs to be initialized if it is in a body less package*/
end;

begin
bodyless.x := bodyless.x + 100;
dbms_output.put_line('Now the value of the x variable of bodyless package is ' ||
bodyless.x);

bodyless.y := bodyless.y + 500;


dbms_output.put_line('Now the value of the y variable of bodyless package is ' ||
bodyless.y);

end;
/* Note-- Execute the block 3-4 times, you will see that the values of x and y are
getting changed.
But disconnect or close the session and start the session again.
The original values of the x and y from the package gets initialized.
*/

90
6) Example of private members in package body –

create or replace package prvmembers


is
procedure m1;
end;

create or replace package body prvmembers


is
/*Creating Private Procedure in the body*/
procedure m2
is
begin
dbms_output.put_line('This is the private member of the package body');
end m2;

procedure m1
is
begin
m2;
end;
end;
Example of forward declaration of a private member

create or replace package body prvmembers


is

procedure m2; /* Forward declaration of m2 */


procedure m1
is
begin
m2;
end;

/*Creating Private Procedure in the body*/

procedure m2
is
begin
dbms_output.put_line('This is the private member of the package body');
end m2;
end;

91
If the private member is not getting called in any global members then forward declaration
is not required.

create or replace package body prvmembers


is
procedure m1
is
begin
dbms_output.put_line('abc');
end;
/*Creating Private Procedure in the body*/
procedure m2
is
begin
dbms_output.put_line('This is the private member of the package body');
end m2;
end;
/

One time only procedure (Procedure in package body)


Useful for setting remote connectivity for front ends.
In the session for the first time any of the members of the package is called then the
procedure will be implicitly invoked.
Then for the future calls of the members of the same package in the same session the
procedure will not get executed.

create or replace package pk1


is
procedure p1;
procedure p2;
end;

92
create or replace package body pk1
is
procedure p1
is
begin
dbms_output.put_line('p1');
end p1;

procedure p2
is
begin
dbms_output.put_line('p2');
end p2;

begin
dbms_output.put_line('Welcome to my package');

end pk1;

create or replace package pk1


is
type namelist is table of emp.ename%type index by binary_integer;
v namelist;
cursor cf is select ename from emp;
M cf%rowtype;
procedure first_five;
procedure next_five;

end;

93
create or replace package body pk1
is
i Number;
procedure first_five
is
begin
for i in 1..5
loop
dbms_output.put_line(v(i));
end loop;

end first_five;
procedure next_five
is

begin
for i in 6..10
loop
dbms_output.put_line(v(i));
end loop;
end next_five;

begin
i := 0;
open cf;

loop
i := i + 1;
fetch cf into M;
v(i) := M.ename;
exit when cf%notfound;

end loop;
end

To initialize the global variable by taking value from table

create or replace package pk1


is
average_sal number;
function calc_sal_deviation(s number) return number;
procedure sal_status(s number);

94
end;

create or replace package body pk1


is
/*function calc_sal_deviation(s number) return number;
procedure sal_status(s number);*/
function calc_sal_deviation(s number) return number
is
begin
if average_sal > s then
return (average_sal - s);
else
return 0;
end if;
end;
procedure sal_status(s number)
is
begin
if average_sal > s then
dbms_output.put_line('On Lower Side');
else
dbms_output.put_line('On Higher Side');
end if;
end;
begin
select avg(sal) into average_sal
from emp;
end;

exec pk1.SAL_STATUS(800);

exec pk1.SAL_STATUS(4000);

select sal, pk1.CALC_SAL_DEVIATION(sal) "Deviation" from emp;

DBMS_SESSION Package

1) The global variable’s value can be re initialized.

create or replace package p


as
g number := 0;
end;

95
/

create or replace procedure pr1


is
begin
p.g := p.g + 1;
dbms_output.put_line('Value of g is ' || p.g);
end;
/

exec pr1;

exec pr1;

exec pr1;

After executing the modify_package_state procedure the initial value of the global
variable g gets initialized.

96
2) Cursor’s can get reinitialized

The cursors which are already open in that session can naturally get closed after closing
the session only. But if the cursor needs to be explicitly closed then the
dbms_session.modify_package_state procedure will do that since the session is getting
reinitialized.

create or replace package pack1


as
cursor cf is select ename,sal from emp;
procedure p1;
end pack1;
/

create or replace package body pack1


as
procedure p1
is
M cf%rowtype;
begin
open cf;
fetch cf into M;
dbms_output.put_line(M.ename || ' - ' || M.sal);
end;
end;
/

exec pack1.p1 -- Shows the first record

exec pack1.p1 -- Throws the error

begin
dbms_session.modify_package_state(dbms_session.reinitialize);
end;

97
/

Set ServerOutPut On

exec pack1.p1

3) Re-initializing the One-time procedure values

Suppose the one time procedure loads the names of the employees into a plsql table using
a cursor, then the names list would be very much session specific.
If the names are updated independently and committed still the same old name list would
be continued.
To refresh the plsql table the dbms_session.modify_package_state has to be used.

Example –

create or replace package pack1


as
type emp_list is table of Emp.Ename%type index by Binary_Integer;
cursor cf is select ename from emp;
procedure show_name;
V emp_list;
end;
/

create or replace package body pack1


as
i number := 1;
procedure show_name
is
begin
For a in V.First .. V.last
Loop
dbms_output.put_line(V(a));
End Loop;
end;
Begin
Open cf;
loop
Exit when cf%notfound;
Fetch cf into V(i);
i := i + 1;
end loop;
end;
/

98
exec pack1.show_name;

update emp
set ename = 'SMITH123'
where ename = 'SMITH';
Commit;

exec pack1.show_name; -- Even if committed still shows SIMTH

begin
dbms_session.modify_package_state(dbms_session.reinitialize);
end;
/
Set ServeroutPut On

exec pack1.show_name; -- Now shows the changed name SMITH123 !

99
8) Example of Overloaded Members

create or replace package pk1


is
procedure p(a number, b number);
procedure p(x varchar2,y varchar2);
procedure p(z number);
procedure p;
end pk1;

create or replace package body pk1


is
procedure p(a number, b number)
is
c number;
begin
c := a + b;
dbms_output.put_line(c);
end p;

procedure p(x varchar2, y varchar2)


is
begin
dbms_output.put_line(x ||y);
end p;
procedure p(z number)
is

begin
if z > 0 then
dbms_output.put_line('The number is positive');
else
dbms_output.put_line('The number is negative');
end if;
end p;

procedure p
is
begin
dbms_output.put_line('No parameter is passed');
end p;
end pk1;

100
Example of overloaded members –
Parameter data types can be same, but names should be different.

create or replace package pk2


as
procedure p(salary number, commission number);
procedure p(salary number, tax number);
end pk2;

create or replace package body pk2


as
procedure p(salary number, commission number)
is
total number;
begin
total := salary + commission;
dbms_output.put_line(total);
end;
procedure p(salary number, tax number)
is
take_home number;
begin
take_home := salary - tax;
dbms_output.put_line(take_home);
end;
end pk2;

While executing such procedure the parameters need to be


called by named method only.
exec pk2.p(salary => 4000, commission => 500);

exec pk2.p(salary => 4000, tax=> 200);

Function Overloading:

CREATE OR REPLACE PACKAGE PK1


IS
FUNCTION F (A NUMBER, B NUMBER) RETURN NUMBER;
FUNCTION F (A NUMBER, C NUMBER) RETURN VARCHAR2;
FUNCTION F (A NUMBER, D NUMBER) RETURN BOOLEAN;
END;

CREATE OR REPLACE PACKAGE BODY PK1


IS
FUNCTION F (A NUMBER, B NUMBER) RETURN NUMBER
IS
BEGIN
IF A >B THEN

101
RETURN 100;
ELSE
RETURN 50;
END IF;
END;
FUNCTION F (A NUMBER, C NUMBER) RETURN VARCHAR2
IS
BEGIN
IF A >C THEN
RETURN 'A IS GREATER THAN B';
ELSE
RETURN 'B IS GREATER THAN A';
END IF;
END;
FUNCTION F(A NUMBER, D NUMBER) RETURN BOOLEAN
IS
BEGIN
IF A >D THEN
RETURN TRUE;
ELSE
RETURN FALSE;
END IF;
END;
END;

102
Example of Persistent Cursors –

create or replace package pc


is
cursor cf is select * from emp;
procedure pp1;
procedure pp2;
c cf%rowtype;
end ;

create or replace package body pc is


procedure pp1 is
begin
open cf;
loop
fetch cf into c;
dbms_output.put_line(c.empno||'-'||c.ename||'-'||c.sal);
exit when cf%rowcount >= 3;
end loop;
end pp1;
procedure pp2 is
begin
loop
fetch cf into c;
dbms_output.put_line(c.empno||'-'||c.ename||'-'||c.sal);
exit when cf%rowcount >= 6;
end loop;
close cf;
end pp2;
end;

103
Function returning an array
create or replace package emp_pack
is
type emp_details is table of emp%rowtype index by binary_integer;
function get_details return emp_details;
end emp_pack;

create or replace package body emp_pack


is

function get_details return emp_details


is
V emp_details;
n integer := 0;
begin
for j in (select * from emp)
loop
n := n +1;
V(n) := j;
end loop;
return V;
end get_details;

end emp_pack;

declare
X emp_pack.emp_details;
begin
X := emp_pack.get_details;
for i in X.first .. X.last
loop
dbms_output.put_line(X(i).ename || ' - ' || X(i).sal);
end loop;
end;
/

104
PURITY LEVELS

PRAGMA RESTRICT_REFERENCES

WNDS - Write No Database State

RNDS - Read No Database State

WNPS - Write No Package State

RNPS - Read No Package State

Example of WNDS and RNDS


create table pt1(a number);

Insert into pt1 values(1);

Package Specification Code –

create or replace package pragma_package


as

function getTotal(commission in number) return number;

PRAGMA RESTRICT_REFERENCES (getTotal, WNDS, RNDS);

end;

105
Package Body Code -

create or replace package body pragma_package


as
function getTotal(commission in number)
return number
is
salary number := 0;
begin
select a into salary from pt1 where a = 1; -- Throws error
return (commission + salary);
end;
end;

PLS-00452: Subprogram 'GETTOTAL' violates its associated pragma

Example of WNPS
Package Code –

create or replace package purity_levels


as
pi number := 3.14;
function circle_area(radius number) return number;
PRAGMA RESTRICT_REFERENCES (circle_area, WNPS);
end;
Package Body code -
create or replace package body purity_levels
as
function circle_area(radius number)
return number
is
area number;
begin
pi := pi + 5.4; -- Throws error
area := pi *radius *radius;

106
return area;
end;
end;

Example of RNPS

Package Specification Code –

create or replace package purity_level1


as
selling_price number := 4000;
tax number := 50;
procedure cust_details;
PRAGMA RESTRICT_REFERENCES (cust_details, RNPS);
end;

Package Body Code --


create or replace package body purity_level1
as
procedure cust_details
is
begin
Insert into pt1 values(purity_level1.selling_price);
end;
end;

-----------------------------

107
NOCOPY Compiler Hint

Suppose a subprogram declares an IN parameter, an OUT parameter, and an IN OUT


parameter. When you call the subprogram, the IN parameter is passed by reference. That
is, a pointer to the IN 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 default, the OUT and IN OUT parameters are passed by value. That is, the value of the
IN OUT actual parameter is copied into the corresponding formal parameter. 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.

Case 1: Without the NOCOPY hint

create or replace package pack1


is
type nametab is table of varchar2(50) index by binary_integer;
procedure p1(a in out nametab);
end pack1;

create or replace package body pack1


is
procedure p1(a in out nametab)
is
begin
for i in 1..a.count
loop
dbms_output.put_line(a(i));
end loop;
end p1;
end pack1;

108
Implementation:

declare
i pack1.nametab;
c number:=1;
begin
for x in (select ename from emp)
loop
i(c) := x.ename;
c := c + 1;
end loop;
pack1.p1(i); /* Here since the NOCOPY is not mentioned all the
values are copied into the procedure. This will slow down and
performance wise not good */
end;

Case 2: With NOCOPY hint

create or replace package pack1


is
type nametab is table of varchar2(50) index by binary_integer;
procedure p1(a in out NOCOPY nametab);
end pack1;

create or replace package body pack1


is
procedure p1(a in out NOCOPY nametab)
is
begin
for i in 1..a.count
loop
dbms_output.put_line(a(i));
end loop;
end p1;
end pack1;

declare
i pack1.nametab;

109
c number:=1;
begin
for x in (select ename from emp)
loop
i(c) := x.ename;
c := c + 1;
end loop;
pack1.p1(i); /* Here due to the NOCOPY compiler hint the
array is again not copied into the procedure but the reference of that
array is given. This is good for performance. */
end;

110
Topic No.9 TRIGGERS Index

 It is a stored PL/SQL program unit associated with a specific database


table
 Oracle executes (fires) the trigger automatically whenever a given
SQL operation affects the table
 They are invoked implicitly
 They are useful for customizing a database
 They should be used only when necessary
 Can automatically generate derived column values
 Can prevent invalid transactions
 Can enforce complex security authorizations
 Can enforce referential integrity across nodes in a distributed database
 Can enforce complex business rules
 Can provide transparent event logging
 Can provide sophisticated auditing
 Can maintain synchronous table replicates
 Can gather statistics on table access
 Can derive column values automatically
 Can restrict DML operations to regular business hours

Syntax --
CREATE [OR REPLACE] TRIGGER <triggername>
BEFORE|AFTER
INSERT|DELETE|UPDATE OF <columnnames> ON <tablename>
[FOR EACH ROW]
WHEN (<condition>)
<PL/SQL Block>

Name in the ON clause identifies the database table associated with the
trigger
The trigger event specifies the SQL DML statement (INSERT, DELETE or
UPDATE) that affects the table
AFTER specifies that the trigger fires after the manipulation is done
 BEFORE specifies that the trigger fires before the manipulation is
done
 By default, a trigger fires once per table
 FOR EACH ROW specifies that the trigger fires once per row

111
 For the trigger to fire, the Boolean expression in the WHEN clause
must evaluate to TRUE
 REPLACE can be added to the CREATE statement to drop and re-
create the trigger automatically
CREATE TRIGGER flight_update
AFTER INSERT ON reservation
FOR EACH ROW
BEGIN
IF :new.class = ‘F’ THEN
statements;
ELSIF :new.class = ‘B’ THEN
statements;
END IF;
END;

 Prefix :new is a correlation name that refers to the newly updated


column value
 Within a trigger, the :new and :old values of changing rows can be
referenced
 A single trigger can handle more than one operation
 Use conditional predicates to identify the type of statement used to
invoke the section of code

Examples of Triggers -
Row Level
AFTER clause

-- Main table
create table temp
as
select * from emp;

-- Table to transfer inserted record


create table instemp
as
select ename,sal
from emp

112
where 1=2;

Whenever a row is inserted in the temp table then that new row
should be transferred in the instemp table

Create or replace trigger trig1


After INSERT on temp
For Each Row
Begin
Insert into InsTemp
Values(:new.ename, :new.sal);
Dbms_output.put_line('Record inserted');
End;

Table to transfer deleted record


create table deltemp
as
select ename,sal
from emp
where 1=2;
--Whenever a row is deleted from temp table then that row should be
transferred --in Deltemp table

Create or replace trigger Trig2


After DELETE on Temp
For Each Row
Begin
Insert into Deltemp
Values(:old.ename, :old.sal);
End;

Table to transfer the old record before updations

create table salary_revision


(empid integer,
Oldsalary number,
Newsalary number);

113
Whenever a record is updated from the temp table then the previous and
the changed salary of that employee should go in table Salary_Revision

create or replace trigger trig3


After UPDATE on temp
For Each Row
Begin
Insert into Salary_Revision
Values(:old.empno, :old.sal, :new.sal);
End;

Table Level vs Column level update trigger

Drop table update_tab;

create table update_tab


(a int, b int, c int);

create or replace trigger trig_all_columns


after update on update_tab
for each row
begin
dbms_output.put_line('Table is updated');
end;

insert into update_tab values(1,2,3);

update update_tab
set a = 7;

update update_tab
set b = 9;

Drop trigger trig_all_columns;

create or replace trigger trig_specific_column


after update of a on update_tab
for each row
begin
dbms_output.put_line('A column got updated');
end;

114
update update_tab
set b = 79;

update update_tab
set a = 9;

Use Of Before Clause


When a new record is inserted then it should have sal >= 5000

Create or replace trigger trig7


Before INSERT on temp
For Each Row
Begin
If :new.sal < 5000 then
raise_application_error(-20009,'New record sal should be above
5000');
End If;
End;

To prevent user from deleting records of sal >= 3500

Create or replace trigger trig4


Before DELETE on temp
For Each Row
Begin
if :old.sal >= 3500 Then
raise_application_error(-20005,'Cannot delete records of sal greater
than or equal to 3500');
End if;
End;

If a salary is updated then it should be always greater than the old salary.

create or replace trigger check_sal


before update of sal
on temp
for each row

115
begin
if :new.sal < :old.sal then
raise_application_error(-20009,'Not possible');
end if;
end;

Whenever a new record is inserted then the cost value should be always
greater than all the existing cost values in the table.

Involving a local variable in the trigger code.

create table cost_chart


(year varchar2(20) unique,
cost number);

create or replace trigger check_cost


before insert on cost_chart
for each row
declare v_cost number;
begin
select max(cost) into v_cost from cost_chart;
if :new.cost < v_cost then
raise_application_error(-20010,'Cost cannot be less than the previous cost');
end if;
end;

Before Vs. After Triggers

Drop Table Mydata;

create table Mydata


(id integer, amt integer);

create or replace trigger Change_Amt_Before


Before Insert on mydata
for each row
begin
:new.amt := :new.amt + 1000;
end;
/

Insert Into mydata Values(1,50);

116
select * from mydata;

SQL> select * from mydata;

ID AMT
---------- ----------
1 1050

create or replace trigger Change_Amt_After


After Insert on mydata
for each row
begin
:new.amt := :new.amt + 2500;
end;
/
ERROR at line 1:
ORA-04084: cannot change NEW values for this trigger type

Sequence of Trigger Error and Constraint Error Execution:

create table abc1(a int check (a >= 100));

create or replace trigger Ch_abc1


before insert on abc1
for each row
begin
if :new.a < 100 then
raise_application_error(-20002,'Wrong Value');
end if;
end;

Insert Into abc1 Values(89); -- Trigger Error

drop trigger Ch_abc1;

create or replace trigger Ch_abc1


after insert on abc1
for each row
begin
if :new.a < 100 then
raise_application_error(-20002,'Wrong Value');
end if;
end;

117
Insert Into abc1 Values(89); -- Check Constraint Error

Exception handling in triggers

drop table t1;


drop table t2;
create table t1(a int);
create table t2(a int check (a >= 10));

create trigger trig_t1


after insert on t1
for each row
begin
insert into t2 values(:new.a);
end;

insert into t1 values(1);

select * from t1;

Drop trigger trig_t1;

create trigger trig_t1


after insert on t1
for each row
begin
insert into t2 values(:new.a);
exception
when others then
null;
end;

insert into t1 values(1);

select * from t1;

select * from t2;

Statement OR Table Level Triggers

1) -- To prevent a new record to be inserted in temp table

118
create or replace trigger trig6
Before INSERT on temp
Begin
raise_application_error(-20006,'Cannot insert a new record');
End;

2) -- To prevent any record to get deleted from temp table

create or replace trigger trig8


Before DELETE on temp
Begin
raise_application_error(-20011,'Cannot delete');
End;

3) -- No transaction should take place on SUNDAY

create or replace trigger trig9


Before INSERT or UPDATE or DELETE
on temp
Begin
If to_char(Sysdate,'DAY') = 'SUNDAY' Then
raise_application_error(-20015,'No transactions on Sunday');
End If;
End;

Demo of sequence of execution of row level and statement level triggers

create table trigtab(a number);

create or replace trigger tt1


before insert on trigtab
begin
dbms_output.put_line('Before statement level');
end;

119
create or replace trigger tt2
before insert on trigtab
for each row
begin
dbms_output.put_line('Before row level');
end;

create or replace trigger tt3


after insert on trigtab
for each row
begin
dbms_output.put_line('After row level');
end;

create or replace trigger tt4


after insert on trigtab
begin
dbms_output.put_line('After Statement level');
end;

120
Multiple Triggers on the Same table’s same event with the same mode of execution

Drop Table Tab1;

CREATE TABLE TAB1(A INT);

CREATE OR REPLACE TRIGGER TRIG1_ON_TAB1


AFTER INSERT
ON TAB1
FOR EACH ROW
BEGIN
DBMS_OUTPUT.PUT_LINE('A');
END;
/

CREATE OR REPLACE TRIGGER TRIG2_ON_TAB1


AFTER INSERT
ON TAB1
FOR EACH ROW
BEGIN
DBMS_OUTPUT.PUT_LINE('B');
END;
/

CREATE OR REPLACE TRIGGER TRIG3_ON_TAB1


AFTER INSERT
ON TAB1
FOR EACH ROW
BEGIN
DBMS_OUTPUT.PUT_LINE('C');
END;
/

INSERT INTO TAB1 VALUES(1);

Note  If any of the trigger has run-time error then none of the triggers will get
executed.

121
Mutating table error
When a row level trigger is based on a table then the trigger body cannot
read data from the same table. Also DML on the same table is not possible.

create or replace trigger trig11


after delete on temp1
for each row
declare
x number;
begin
select sal into x from temp1
where ename = 'SMITH';
dbms_output.put_line(x);
end;

delete from temp1;

create or replace trigger trig11


after delete on temp1
for each row
begin
insert into temp1 (empno,ename) values(78,'pqr');
end;

delete from temp1 where ename = 'KING';

122
For a statement level trigger there will be no error.

create or replace trigger trig11


after delete on temp1
declare
x number;
begin
select sal into x from temp1
where ename = 'SMITH';
dbms_output.put_line(x);
end;

delete from temp1


where ename = 'MARTIN';

create or replace trigger trig11


after delete on temp1
begin
insert into temp1 (empno,ename) values(78,'pqr');
end;
/

delete from temp1 where ename = 'KING';

123
Combining DML events in one Trigger

Any DML should be traced in the table KeepTrace

create table KeepTrace


(username varchar2(30),
Operation varchar2(30),
Date_of_Operation date,
Time_of_operation varchar2(40));

Create or replace trigger trig10


After INSERT or DELETE or UPDATE
On Temp
For each row
Begin
If Inserting Then
Insert into KeepTrace
Values(user, 'Record is Inserted', Sysdate, to_char(sysdate,'hh:mi:ss'));
ElsIf Deleting Then
Insert into KeepTrace
Values(user, 'Record is Deleted',Sysdate, to_char(sysdate,'hh:mi:ss'));
ElsIf Updating Then
Insert into KeepTrace
Values(user,'Record is Updated',Sysdate,
to_char(sysdate,'hh:mi:ss'));
End If;
End;

124
Calling a procedure inside a trigger

create table EmpSummary


(Total_Salary number);

Procedure Code –

create or replace procedure TotSal


is
vsal number;
Begin
delete from EmpSummary;
Select sum(sal) into vsal
from emp;
Insert into EmpSummary
Values(vsal);
End;

create or replace trigger EmpUpdates


After insert or update on emp
CALL TOTSAL

Or if the trigger has it’s code then –

create or replace trigger EmpUpdates


After insert or update on emp
Begin
-- Trigger Code
-- Trigger Code
TotSal
-- Trigger Code
End

125
Instead Of Triggers

 Provides a transparent way of modifying views that cannot be


modified directly through INSERT, UPDATE or DELETE statements
because underlying tables contain joins
 It is called INSTEAD OF because Oracle fires the trigger instead of
the triggering statement(s)
 Users can be transparent to the trigger because they write normal
DML statements against the view and the INSTEAD OF trigger is
used to take care of the modifications
 It can be placed on Object Views to insert, update or delete data in the
underlying relational tables
CREATE TRIGGER emp_insert
INSTEAD OF INSERT ON emp_view
BEGIN
statements;
END;

Instead of triggers can be used only with views.


Effective for joins which are based on equi join
To have an cascading effect of update on both the tables if columns are
-matching

--Step 1 Creating tables s and r;


create table s
(rollno number,
name varchar2(20));

create table r
(rollno number,
marks number);

--Step 2 Inserting records in s and r.


insert into s
values(1,'a');
insert into s
values(2,'b');

insert into r

126
values(1,90);
insert into r
values(2,87);

--Step 3 Creating an Equijoin View on s and r

Create or replace view SR


as
select s.rollno,s.name,r.marks
from s,r
where s.rollno =r.rollno;

Example of Instead of Insert


Create or replace trigger tig14
Instead Of INSERT on SR
For Each Row
Begin
/*Inserting the new record into both the tables s and r.*/
Insert into s
values(:new.rollno, :new.name);

Insert into r
values(:new.rollno, :new.marks);

End;

DDL EVENT TRIGGERS

1) To prevent a Scott from dropping table TT

create or replace trigger prevent_drop


Before Drop on Scott.Schema
Begin
If ora_dict_obj_owner = 'SCOTT'
and
ora_dict_obj_name = 'TT'
and
ora_dict_obj_type = 'TABLE'
Then

127
raise_application_error(-20020,'Cannot drop table TT');
End If;
End;

2) To keep a track of Scott new objects


The new objects should get added in a table ScottObjects
ScottObjects is owned by DBA

create table ScottObjects


(Object_name varchar2(30),
Date_of_Creation date);

create or replace trigger Put_New_Objs


After CREATE ON Scott.Schema
Begin
Insert into ScottObjects
Values(ora_dict_obj_name,Sysdate);
End;

3) To keep a track of Scott's dropped objects. The dropped objects should get
added in a table ScottDrop

create table ScottDrop


(Object_name varchar2(30),
Date_of_Creation date);

create or replace trigger Put_Drop_Objs


After DROP ON Scott.Schema
Begin
Insert into Scottdrop
Values(ora_dict_obj_name,Sysdate);
End;

4) Preventing Scott to drop column h2 of table hh


Step 1 Login as scott
create table hh
(h1 number,
h2 number,
h3 number);

128
Step 2 Log in as sysdba

Create or Replace Trigger Prev_Drop_Col


Before ALTER on Scott.Schema
Begin
If ora_dict_obj_name = 'HH'
and
ora_is_drop_column('H2')
Then
raise_application_error(-20067,'Cannot drop column h2');
End If;
End;

5) --To prevent Scott from modifying the data type of column h3 of hh table
Log in as DBA

create or replace trigger Prev_Modify


Before ALTER on Scott.Schema
Begin
If ora_dict_obj_name = 'HH'
and
ora_is_alter_column('H3')
Then
raise_application_error(-20045,'Cannot modify column H3');
End If;
End;

129
Autonomous Transactions with Triggers

Normally in a trigger we cannot provide commit or rollback.


But some times when a trigger is doing the auditing work then record of audit table
should get committed but the original transaction should get rolled back.
Now this is only possible if pragma autonomous_transaction clause is provided in the
trigger definition.

Example 1 –

We have to make a trigger, which will ensure that the changed (updated) salary should
not exceed thrice of the current (old) salary.

Steps –

1) Create the audit_table

create table audit_table


(username varchar2(10),
date_of_action date,
time_of_action varchar2(20),
old_salary number,
new_salary number,;

2) create the trigger

create or replace trigger bu


before update of sal on emp
for each row
declare
pragma autonomous_transaction;
begin
if :new.sal >= :old.sal * 3 then
insert into audit_table
values(user,sysdate, to_char(sysdate,'hh:mi:ss'), :old.sal, :new.sal,:old.ename);
commit;

raise_application_error(-20001, 'You have tried to do something you


should not have and we know it');
end if;

end;
/

130
Step3 – Now try to update Smith’s salary. His current salary is 800, so provide the new
salary as 3200

update emp
set sal = 3200
where ename = 'SMITH'; -- Throws error

select * from audit_table;

131
Topic 10. Dynamic SQL Index

What Is Dynamic SQL?

Dynamic SQL enables you to write programs those reference SQL statements whose
full text is not known until runtime. Before discussing dynamic SQL in detail, a clear
definition of static SQL may provide a good starting point for understanding dynamic
SQL. Static SQL statements do not change from execution to execution. The full text of
static SQL statements are known at compilation, which provides the following benefits:

 Successful compilation verifies that the SQL statements reference valid database
objects.
 Successful compilation verifies that the necessary privileges are in place to access
the database objects.
 Performance of static SQL is generally better than dynamic SQL.

Because of these advantages, you should use dynamic SQL only if you cannot use static
SQL to accomplish your goals, or if using static SQL is cumbersome compared to
dynamic SQL. However, static SQL has limitations that can be overcome with dynamic
SQL. You may not always know the full text of the SQL statements that must be executed
in a PL/SQL procedure. Your program may accept user input that defines the SQL
statements to execute, or your program may need to complete some processing work to
determine the correct course of action. In such cases, you should use dynamic SQL.

For example, a reporting application in a data warehouse environment might not know
the exact table name until runtime. These tables might be named according to the starting
month and year of the quarter, for example INV_01_1997, INV_04_1997, INV_07_1997,
INV_10_1997, INV_01_1998, and so on. You can use dynamic SQL in your reporting
application to specify the table name at runtime.

You might also want to run a complex query with a user-selectable sort order. Instead of
coding the query twice, with different ORDER BY clauses, you can construct the query
dynamically to include a specified ORDER BY clause.

Dynamic SQL programs can handle changes in data definitions, without the need to
recompile. This makes dynamic SQL much more flexible than static SQL. Dynamic SQL
lets you write reusable code because the SQL can be easily adapted for different
environments.

Dynamic SQL also lets you execute data definition language (DDL) statements and
other SQL statements that are not supported in purely static SQL programs.

132
Why Use Dynamic SQL?

You should use dynamic SQL in cases where static SQL does not support the operation
you want to perform, or in cases where you do not know the exact SQL statements that
must be executed by a PL/SQL procedure. These SQL statements may depend on user
input, or they may depend on processing work done by the program. The following
sections describe typical situations where you should use dynamic SQL and typical
problems that can be solved by using dynamic SQL

Executing DDL and SCL Statements in PL/SQL

In PL/SQL, you can only execute the following types of statements using dynamic SQL,
rather than static SQL:

 Data definition language (DDL) statements, such as CREATE, DROP, GRANT, and
REVOKE
 Session control language (SCL) statements, such as ALTER SESSION and SET ROLE

Executing Dynamic Queries

You can use dynamic SQL to create applications that execute dynamic queries, whose full
text is not known until runtime. Many types of applications need to use dynamic queries,
including:

 Applications that allow users to input or choose query search or sorting criteria at
runtime
 Applications that allow users to input or choose optimizer hints at run time
 Applications that query a database where the data definitions of tables are
constantly changing
 Applications that query a database where new tables are created often

133
DBMS_SQL is used to write dynamic SQL in stored procedure and to parse DDL
statements.

Some Procedures and Functions in DBMS_SQL package are –


Open_Cursor – Opens a new cursor and assigns a cursor ID number.

PARSE – Parses the DDL or DML statements, i.e. it checks the syntax and associates it
with the opened cursor. In case of DDL statements they are executed immediately after
parsing.

PARSE (ID number of the cursor, DDL or DML statement, language_flag)

language_flag Determines how Oracle handles the SQL statement. The following
options are recognized:

 V6 (or 0) specifies version 6 behavior.


 NATIVE (or 1) specifies normal behavior for the database to
which the program is connected.

 V7 (or 2) specifies Oracle7 behavior.

EXECUTE – Executes the SQL statement and returns the number of rows processed.
Especially suitable for keeping track of count of rows affected by a DML statement.

Examples:

create or replace procedure drop_table(p_tablename in varchar2)


is
C NUMBER;
begin
c := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(C,'DROP TABLE ' || p_tablename, DBMS_SQL.NATIVE);
exception
when others then
dbms_output.put_line('Table name to be dropped does not exist');
end;

134
create or replace procedure delete_rows(table_name varchar2)
is
c number;
rows_deleted number;
begin
c := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(c, 'Delete from ' || table_name, DBMS_SQL.NATIVE);
rows_deleted := DBMS_SQL.EXECUTE(c);
dbms_output.put_line('The total number of rows deleted are ' || rows_deleted);
Exception
when others then
dbms_output.put_line('No rows deleted');

end;

Execute immediate:

The Parsing and the execution is done immediately one after the
other.

CREATE PROCEDURE del_rows


(p_table_name IN VARCHAR2,
p_rows_deld OUT NUMBER)
IS
BEGIN
EXECUTE IMMEDIATE 'delete from '||p_table_name;
p_rows_deld := SQL%ROWCOUNT;
END;
/
VARIABLE deleted NUMBER

EXECUTE del_rows('test_employees',:deleted);

PRINT deleted

135
Performing DCL command through triggers.

SQL> create user demo_ddl identified by demo_ddl;


User created.

SQL> grant create user to demo_ddl;


Grant succeeded.
SQL> grant drop user to demo_ddl;
Grant succeeded.

SQL> connect demo_ddl/demo_ddl


Connected.

create table new_users_info


(username varchar2(20),
password varchar2(20));

create or replace trigger NEW_USERS_TRIG


after insert on NEW_USERS
for each row
declare
-- this pragma will allow our trigger to perform DDL
pragma autonomous_transaction;
begin

execute immediate
'Create user ' || :new.username ||
' Identified by ' || :new.pw;
dbms_output.put_line('User is created');

end;

Insert into New_Users values (‘John’,’John’);

Example 3

create table metadata


(tname varchar2(20),
column1 varchar2(30),
dt_col1 varchar2(20),
column2 varchar2(30),
dt_col2 varchar2(20));

create or replace trigger create_table

136
after insert on metadata
for each row
declare
pragma autonomous_transaction;
begin
execute immediate
'Create Table ' || :new.tname || '(' ||
:new.column1 || ' ' || :new.dt_col1 || ','||
:new.column2 || ' ' || :new.dt_col2 ||
')' ;
end;
/

DBA should provide create any table privilege to the current user for
having the effect of the above trigger.

Logon as DBA

grant create any table to demo_ddl;

Create any table is a system privilege

conn demo_ddl/demo_ddl

insert into metadata


values ('try1','name','varchar2(20)','marks','number');

desc try1

137
Example 4
Trigger for dropping a table

create or replace trigger drop_table


after delete on metadata
for each row
declare
pragma autonomous_transaction;
begin
execute immediate
'Drop Table ' || :old.tname;
end;
/

138
Topic 11. DBMS_DDL Package Index

Scenario of recompilation for a procedure –

drop table tx1;

create table tx1(a number);

insert into tx1 values(1);

create or replace procedure px1


is
s number;
begin
select a into s
from tx1;
end;

select status from user_objects


where object name='PX1';

alter table tx1


add b number

select status from user_objects


where object_name='PX1';

alter procedure px1 compile; OR Exec px1;

select status from user_objects


where object_name='PX1';

139
DBMS_DDL.ALTER_COMPILE(‘object_type’, ‘owner name’, object_name)

Object type can be a procedure, function, package specification, package body or a


trigger only.

create or replace procedure compile procedure(procedure name varchar2)


as
begin
DBMS_DDL.ALTER_COMPILE('PROCEDURE','SCOTT', procedure name);
dbms_output.put_line('The procedure ' || procedure name || ' is recompiled');
end;

exec compile procedure('PX1');

select status from user_objects


where object name='PX1';

140
Topic 12. DBMS_JOB Package Index

Enables the scheduling and execution of PL/SQL programs.

Submit is the procedure of DBMS_JOB package.


The parameters of Submit procedure are ---

JOB – Unique identifier of the job (Out parameter)


WHAT – PL/SQL code to execute as a job
NEXT_DATE – Next execution date of the job
INTERVAL – Date function to compute the next execution date of a job.

Example –
Every next day a new record should get added in the table djt1.

Create table djt1(a number);

Create sequence jobseq;

Create or replace procedure p1


Is
Begin
Insert into djt1
Values(jobseq.nextval);
End;
/

Variable jobno number

begin
dbms_job.submit
(
job => :jobno,
what => 'P1;',
next_date => trunc(sysdate),
interval => 'trunc(sysdate + 1)'
);
commit;
end;

141
In the init.ora file JOB_QUEUE_PROCESSES = n has to
be setted
n can be between 1 and 36.

Change the system date to the next date. The t1 table gets the next
value of the sequence.
Again repeat the process.

Imp setting – job_queue_processes = n (n between 1 and 36) in the init.ora file

Select job,what from user_jobs;

Exec dbms_job.Broken(1,true) – 1 is job no

Exec dbms_job.Remove(1)

Dbms_job.change(jobno, what, next_date, interval)

Jobno cannot be changed.


If there is no change for what, next_date, interval then specify NULL

For having interval of 60 seconds it will be ‘Sysdate + (59/ (24*60*60))’

142
Topic 13. UTL_FILE Package Index

UTL_FILE package is used to write data to operating system files such as


text files, word files, excel files, etc.

The data from the oracle table is transferred to the o.s. files. So we can
generate a report of Oracle data in a non-oracle format.

Members of UTL_FILE Package are as follows –

FOPEN function

This function opens a file for input or output. The file location must be an
accessible directory, as defined in the instance's initialization parameter
UTL_FILE_DIR. The complete directory path must already exist; it is not
created by FOPEN.

It takes 3 parameters
1. Directory name  The path
2. File name (.txt, .doc,.xls,)
3. Open_Mode 
a. ‘r’ – Read Text
b. ‘w’-- Write Text
c. ‘a’ – Append Text

PUTF procedure

This procedure is a formatted PUT procedure. It works like a limited


printf(). The format string can contain any text, but the character sequences
'%s' and '\n' have special meaning.

%s Substitute this sequence with the string value of the next argument in the argument list.

\n Substitute with the appropriate platform-specific line terminator.

FCLOSE procedure
This procedure closes an open file identified by a file handle.

143
NEW_LINE procedure (Can specify how many blank lines are required.
Default is 1)
This procedure writes one or more line terminators to the file identified by
the input file handle. This procedure is separate from PUT because the line
terminator is a platform-specific character or sequence of characters.

Syntax
UTL_FILE.NEW_LINE (
file IN FILE_TYPE,
lines IN NATURAL := 3);

Example of generating a text file of salary status from the emp table.

create or replace procedure sal_status


(p_filedir in varchar2, p_filename in varchar2)
is
v_filehandle UTL_FILE.FILE_TYPE;
Cursor emp_info is
select ename,sal,deptno
from emp
order by sal desc;
Begin
v_filehandle := UTL_FILE.FOPEN(p_filedir,p_filename,'w');
UTL_FILE.PUTF(v_filehandle,'SALARY REPORT : GENERATED ON
%s\n',SYSDATE);
UTL_FILE.NEW_LINE(v_filehandle);
for v_emp_rec in emp_info
LOOP
UTL_FILE.PUTF(v_filehandle,'DEPARTMENT: %s \n',
v_emp_rec.deptno);
UTL_FILE.PUTF(v_filehandle,' Employee:%s earns: %s\n',
v_emp_rec.ename,v_emp_rec.sal);

END LOOP;
UTL_FILE.PUTF(v_filehandle, '*** END OF REPORT ***');
UTL_FILE.FCLOSE(v_filehandle);
EXCEPTION
WHEN UTL_FILE.INVALID_FILEHANDLE THEN
raise_application_error(-20001,'Invalid File');
WHEN UTL_FILE.WRITE_ERROR THEN
raise_application_error(-20002,'Unable to write file');
End;

Steps for executing the procedure are as follows –

144
1. In the init.ora file of the database, the file location has to be defined
as a value of UTL_FILE_DIR

Open the init.ora file and add the property as follows-


UTL_FILE_DIR = C:\ABC

(Step 1 is only required for early version. If directory object is not


created then UTL_FILE_DIR path is required)
2. Create a folder ABC in C drive.

3. Create a directory object in database as –

create directory D1 as 'C:\ABC'

Directory is a nonschema database object that provides a way for


administering the o.s. files through Oracle Server 9i.
A Directory specifies an alias for the actual o.s. path .

4. Execute the procedure

Exec Sal_Status('D1','a1.txt'); -- a1.txt is the new text file getting


generated.

Query the following dictionary views to get information on directories in


the database

ALL_DIRECTORIES - all directories accessible to the database user

145
Topic 14. Bfile and LOBs Index

Bfile – Binary file (text, sound, image or video) is stored outside the database. The
locator to that file is stored in the database. Bfile data is read only for oracle.

LOB – BLOB and CLOB are the tow types of LOBs. B is binary and C is character data.
LOB files are stored inside the database. Since the files are inside db, they can be
manipulated thorough oracle. A max of 4GB data can be stored in a BLOB or CLOB data
type.

Directory is a non-schema object owned by “SYS” user. It is used for managing the
operating system files (that are external to oracle). Directory object will help to write data
into external file or read data from an external file.

BfileName function – It initializes a Bfile column to point to an external file. Use the
BfileName function as part of an Insert statement to initialize a Bfile column by
associating it with a physical file in the server file system. A Bfile can be initialized to
NULL and updated later by using the BFILENAME function.

FileOpen -- To open an external file.

LOADFROMFILE – To load Bfile data into an internal LOB.

Empty_CLOB – It is a special function, which can be used in Insert or Update statement


of SQL DML to initialize a NULL.

FileClose – To close an external file

Example of Reading data from a Bfile and writing it in Oracle

Step 1- There is file mytextfile.txt in C:\MyData path. The mytextfile.txt has some
sample text.

Step 2 – A user should have create any directory and read directory privileges. These
privileges are taken from SYS user.

Step 3 - Create directory

146
Step 4 – Create a table mytable with two fields file_id and file_data

Create table mytable


(file_id number,
file_data clob);

Step 5 – A procedure will take file_id and filename from user and transfer data of that file
into the table mytable.

147
CREATE OR REPLACE PROCEDURE insertclob (p_file_id number, filename
VARCHAR2)
AS
v_clob CLOB;
v_bfile BFILE;
BEGIN
INSERT INTO mytable VALUES (p_file_id,empty_clob());
COMMIT;
v_bfile:=BFILENAME('EXTERNAL_FILE11',filename);

select file_data INTO v_clob FROM mytable WHERE file_id=p_file_id FOR


UPDATE;
DBMS_LOB.FILEOPEN(v_bfile);

DBMS_LOB.LOADFROMFILE(v_clob,v_bfile,DBMS_LOB.GETLENGTH(v_bfile));
DBMS_LOB.FILECLOSE(v_bfile);
END;
/

Now execute the procedure by providing file id and file name

exec insertclob(1,'mytextfile.txt');

The data of mytextfle.txt has been transferred in the file_data column

148
(Note – In a session, the number of BFILEs that can be opened in one session is limited
by the parameter session_max_open_files and total number of files that can be open is
limited by the parameter max_open_files. These parameters are to be setted in the
init.ora file
session_max_open_files=10
max_open_files=20)

The DBMS_LOB.READ Procedure


 Reads data from LOB starting at specified offset
DBMS_LOB.READ(loc, num, offset, buff)

Where
loc is locator of the LOB
num is number of bytes/characters to be read
offset is offset in bytes/characters from the start of the LOB
buff is output buffer for the read operation

Example – To retrieve a part of data from the first record’s


file_data column

declare
v_clob clob;
num number;
offset number;
vstore varchar2(20);
begin
num := 7;
offset :=1;
select file_data into v_clob
from mytable

149
where file_id = 1;
DBMS_LOB.READ(v_clob,num,offset,vstore);
dbms_output.put_line(vstore);
end;
/

The DBMS_LOB.GETLENGTH Function


 Gets the length of the LOB value
DBMS_LOB.GETLENGTH(loc)

Where
loc is locator of the LOB

Example – To get the length of the file_data

declare
loc clob;
len number;
Begin
select file_data into loc from mytable
where file_id = 1;
len := DBMS_LOB.GETLENGTH(loc);
dbms_output.put_line(len);
end;
/

The DBMS_LOB.WRITE Procedure

 Writes data to the LOB from specified offset


DBMS_LOB.WRITE(loc, num, offset, buff)

Where
loc is locator of the LOB
num is number of bytes/characters to write
offset is offset in bytes/characters from the start of the LOB for
write operation

150
buff is input buffer for the write operation

Example of Write and getLength function –


--DBMS_LOB.Write and getLength Functions
-- To add some text to CLob.
-- It is going to update the original clob
--Syntax DBMS_LOB.Write(Source, Length of new text, From
which character or point to be added, New Text)

Declare
loc clob;
num number;
offset number;
vstore varchar2(80);
lenlob number;
vlen number;
Begin
-- In the Select statement For Update clause is must, so that the row gets locked.
select file_data into loc from mytable where file_id=1 for update;
-- To get the total number of characters in a lob.
-- Use getlength function of DBMS_LOB
lenlob := DBMS_LOB.getLength(loc);
-- vstore is having the additional text
vstore := 'This is new line';
-- Now the length of the new text to be added should also be known since that value is
one
vlen := Length(vstore); -- Length of the new text in vstore variable
offset := lenlob + 1; -- Due to +1 in offset the text starts from --one space next
DBMS_LOB.Write(loc,vlen,offset,vstore);
end;
/

exec insertclob(2,'mytextfile.txt');

Example1of Erase method –

151
DBMS_LOB.Erase

Syntax - DBMS_LOB.Erase(Source, No. of characters to erase,


Position from where it has to erase)

To remove some text from clob column


Removing the first 5 characters of file_data column for file_id 2

Declare
loc clob;
num number;
offset number;
Begin
num := 5;
offset := 1;

select file_data into loc from mytable


where file_id = 1;

DBMS_LOB.Erase(loc,num,offset);
End;
/

152
Topic 15. Managing Dependencies Index

CASE FIRST

CREATE OR REPLACE VIEW V1


AS
SELECT * FROM TX1;

ALTER TABLE TX1


ADD C NUMBER;

DESC V1;

SELECT * FROM V1;

DESC V1;
CASE TWO

CREATE OR REPLACE PUBLIC SYNONYM S22 FOR TX1;

CREATE OR REPLACE PROCEDURE PP5


IS
X NUMBER;
BEGIN
SELECT A INTO X
FROM S22;
END;

CREATE TABLE S22(Y VARCHAR2(10));

EXEC PP5;

DROP TABLE S22;

EXEC PP5;

153
CASE THREE

create or replace procedure query_emp


is
x number;
begin
select sal into x from emp where ename = 'JAMES';
dbms_output.put_line(x);
end;

create or replace view emp_vw


as
select * from emp;

create or replace procedure add_emp


is
begin
update emp_vw
set sal = sal+ 10000
where ename = 'JAMES';
end;

Changes ---
1) The internal logic of the Query_EMP procedure is modified.

create or replace procedure query_emp


is
x number;
begin
select sal into x from emp where ename = 'ALLEN';
dbms_output.put_line(x);
end;

EXEC ADD_EMP;

2) A new column is added to the EMP table.

alter table emp


add xy number;

EXEC ADD_EMP;

3) The EMP_VW is dropped.


drop view emp_vw;

154
EXEC ADD_EMP;

CASE FOUR

Drop package PACKMD;

CREATE OR REPLACE PACKAGE PACKMD


AS
PROCEDURE P1;
END;
CREATE OR REPLACE PACKAGE BODY PACKMD
AS
PROCEDURE P1
is
begin
dbms_output.put_line('AAAA');
end;
END;

create or replace procedure standalone


is
begin
PACKMD.P1;
END;

EXEC STANDALONE

Change the body of the package……

CREATE OR REPLACE PACKAGE BODY PACKMD


AS
PROCEDURE P1
is
begin
dbms_output.put_line('BBBB');
end;
END;

155
select status from user_objects
where object_name='STANDALONE';

Now change the specification of the package……

CREATE OR REPLACE PACKAGE PACKMD


AS
PROCEDURE P1;
procedure p2;
END;

select status from user_objects


where object_name='STANDALONE';

Displaying direct and Indirect dependencies

Step 1 – Run the script utldtree.sql that creates the objects that enable you to display the
direct and indirect dependencies.

(This script is in ORACLE_HOME/rdbms/admin folder)

Scenario – Table is emp

EMP table has two direct dependent objects of type view and trigger.

create or replace view V_Emp


as
select * from emp1
where job = 'MANAGER'
/

create or replace trigger E_Trig


after insert on emp1
for each row
begin
dbms_output.put_line('Record inserted in emp table');
end;
/

There is a procedure P_V_Emp which is based on view V_Emp.


So there is indirect dependency of table emp with P_V_Emp procedure.

create or replace procedure P_V_Emp(no emp1.empno%type, name emp1.ename%type, j


emp1.job%type, s emp1.sal%type DEFAULT 'MANAGER')

156
is
begin
delete from V_EMP;
Insert into V_Emp(empno,ename,job,sal)
values(no,name,j,s);
end;
/

Now there is a procedure P2 which calls P_V_Emp

create or replace procedure p2


is
begin
P_V_Emp(123,'abc', 8000);
end;
/

To get the list of dependencies execute the procedure deptree_fill

EXECUTE deptree_fill('TABLE','SCOTT','EMP1');

DEPTREE VIEW

select nested_level, type, name


from deptree
/

To display an indented representation of all the dependent objects query ideptree view.

157
158
Topic 16. REF CURSORS Index
Limitations of a normal cursors are --
1) A PL/SQL program cannot pass a cursor as a parameter to another program.
2) A PL/SQL program can only open the cursor and process the information within
the program itself.

To overcome these limitations there is a concept of REF CURSOR.

Features of REF CURSOR --


1) There can be a TYPE of ref cursor. The variable of this TYPE can be used to
pass the parameters to a program and return value from the cursor.
2) The variable of REF CURSOR type returns the same data type as the cursor
variable.
3) The cursor variable is passed as a parameter to a procedure.
4) The cursor variable takes all the rows from the specified table.
5) These rows are given to the bind variable.
So the parameter passed should satisfy two conditions --
a) The parameter should be taken from the TYPE of Ref Cursor.
b) It should be of IN OUT mode.
6) Finally the data retrieved by the cursor variable can be seen through the bind
variable. For this the data type of the bind variable should be REFCURSOR.
7) While executing the procedure bind variable should be directly given. And then by
print statement the data is displayed.
8) The cursor variable's data structure and the procedure block's data structure should
be same.

Advantage of REF CURSOR--


Actually we can get the view of the entire data of the table with simplicity using
REF CURSOR.
Without using ref cursor if we have to achieve this then, the parameter passed will
be of variable type
and then the user has to manual loop using cursor to fetch all the records. Here in
REF CURSOR there is no need of looping.

159
Example of REF CURSOR –

Package Specification –

create or replace package PRC as

TYPE EmpRC IS REF CURSOR RETURN emp%rowtype;


TYPE DeptRC IS REF CURSOR RETURN dept%rowtype;

Procedure EmpDetails(ve IN OUT EmpRC);


Procedure DeptDetails(vd IN OUT DeptRC);

End PRC;
Package Body –

create or replace package Body PRC as

Procedure EmpDetails(ve IN OUT EmpRC)


is
Begin
Open ve FOR select * from emp;
End EmpDetails;

Procedure DeptDetails(vd IN OUT DeptRC)


is
Begin
Open vd FOR select * from dept;

End DeptDetails;

End PRC;

For executing the procdure –


1) SQL > variable E REFCURSOR
SQL > variable D REFCURSOR

2) To see the data from the bind variable --


SQL > Set AutoPrint ON

3) SQL > Execute PRC.EmpDetails(:E);


4) SQL > Execute PRC.DeptDetails(:D);

160
Topic 17. TYPES Index

 Users can create their own data types by combining previously defined data types
 Also called Object types
 Object types made up of attributes and members

Creating type Info which holds the attributes –

Create or replace type Info as OBJECT


( Name varchar2(30),
Address varchar2(30),
City varchar2(20),
Phone number(7));

To see the structure of the object -


Desc Info

Creating Relational Table with User-Defined Type

Using this type in table E.


E table becomes a relational table due to the use of Info user defined datatype.

Create table E
(Empno number,
Emp_Det Info,
Sal number);

See the structure of this relational table E

Desc E

To see the attributes in the Desc command for a relational table

To see the attributes and the datatypes of the user defined datatype
set describe depth 2

Desc E

Inserting data in the table E

Insert into E
Values (1, Info(‘abc’,’kothrud’,’Pune’,7876), 7000);

161
Validations to the Column of user defined datatype—
Only Not Null can be given.

create table f57


(a number,
b info not null)

Note – Primary key, Unique, Foreign Key and check constraint cannot be given to
the column of user defined data type.

Constraint can be given to the attributes of the column for that specific table.
For the name attribute of det column giving constraint –
Syntax -- ColumnName.AttributeName

create table j1
(roll number,
det info,
constraint ccc unique(det.name));

Accessing the values of the attribute columns -

To see the name and city for Empno 1 ---


 To view an attribute’s value from user-defined types, dot notation is used
 Alias name is a must for the table
 Values of the tables can be referred using dot notation prefixed with table alias

Select X.Emp_Det.Name, X.Emp_Det.City, Empno


from E X
where Empno = 1;

To see the records when the condition is having an attribute column -

Select * from E X
where X.Emp_Det.Name = ‘abc’;

162
Altering types

Dropping the type—


To drop a type first all the tables in which that type is used should be dropped , then only
the type can be dropped.

User_Types is the in built table for type information.


select type_name,typecode from user_types;

Altering types –

The attribute’s data type size can be increased at any time.

create or replace type h as object


(x number(3),
y number(4));

/*Can increase the size.*/


alter type h
modify attribute x number(10);

/*Cannot decrease the size. */


alter type h
modify attribute x number(9);

Once a attribute is defined in the type then in future it’s data type cannot be
changed. Only its size can be increased.

Can drop an attribute provided the type is not used in a table.

alter type h
drop attribute x;

Adding an attribute after type creation.


create or replace type t1
as object
(a number);

alter type t1
add attribute b number;

163
Introduction to Methods

 A method is a subprogram declared in an object type specification using the


keyword MEMBER
 A method is procedure or function that is part of the object type definition
 A method cannot have the same name as the object type or any of its attributes
 Like packaged programs, most methods have two parts
 Specification
 consists of method name
 an optional parameter list
 for functions, a return type
 Body
 is the code that executes to perform a specific operation
 The methods of an object define the behavior of the object
 These methods are called MEMBER methods
 Member methods are first defined in the object type and then the method body is
created
 Methods are defined in the Object type with the CREATE statement

Syntax for Method Specification--

CREATE [OR REPLACE] TYPE <typename> AS OBJECT


(attribute1 datatype,
:
attributeN datatype
MEMBER PROCEDURE <methodname> (parameter, mode, datatype),
MEMBER FUNCTION <methodname> (parameter, mode, datatype) RETURN datatype;

 Method body is also created with the CREATE statement


CREATE [OR REPLACE] TYPE BODY <typename> AS
MEMBER FUNCTION <methodname> (parameter dataype) RETURN <datatype> IS
<PL/SQL_block>;
MEMBER PROCEDURE <methodname>(parameter datatype);
END;

Procedure inside a type:

create type address as object


(
street varchar2(10),
city varchar2(10),
state varchar2(10),
member procedure changeadd(st_n varchar2, st_c varchar2, st_s varchar2)
);
/

164
create or replace type body address
is
member procedure changeadd(st_n varchar2, st_c varchar2, st_s varchar2)
is
begin
if (st_n is null) or (st_c is null) or st_s is null or
(upper(st_s) not in ('UK','US','CA') ) then
raise_application_error(-20001,'INVAID DATA');
else
street := st_s;
city := st_c;
state := st_s;
end if;
end changeadd;
end;
/

Implementation:
declare
add1 address;
begin
add1 := address('a','b','c'); -- Initializing
add1.changeadd('x','y','z');
dbms_output.put_line(add1.state);
end;
/

declare
add1 address;
begin
add1 := address('a','b','c'); -- Initializing
add1.changeadd('x','y','us');
dbms_output.put_line(add1.state);
end;
/

Function inside a type:


Example 1:

create or replace type area as object


(length_c integer,
breadth_c integer,
height_c integer,
member function cube_cal return integer
);

165
create or replace type body area
as
member function cube_cal
return integer
is
v_area integer;
begin
v_area := length_c * breadth_c * height_c;
return v_area;
end cube_cal;
end;

Implementation:

declare
c area;
a integer;
begin
c := area(3,4,5);
a := c.cube_cal;
dbms_output.put_line('The area is ' || a);
end;
/

Example 2:

Create a function with it’s code inside a type. Create a object table of that type.
Then the function can be called directly in the select statement by passing column
name as a parameter. (E.g. -- Select sum(sal) from emp)
Advantage -- No need of writing the PL/SQL block for calling the function.

Example of Method ---

Step 1 –Creating Method specification --


create or replace type results as object
(name varchar2(50),
marks number,
member function resultsfun(marks In Number) return char
);

Step2 – Creating Method Body ----

166
create or replace type body results as
member function resultsfun(marks In Number) return char is
disp char(20);
begin
if marks >= 35 then
disp := 'Passed';
else
disp := 'Failed';
end if;
return disp;
end;
end;

Step 3 --- Creating Object table student based on the above type –

create table students of results;

Step 4 – Inserting two records ---

insert into students


values('John',90);
insert into students
values('Smith',23);

Step 5 --- Calling Object Methods


 Object methods can be called in a query, after an Object table has been created
based on the type
SELECT <columnname>, <aliasname>.<methodname(parameters)>
FROM <tablename> <aliasname>;

Select name, f.resultsfun(marks) RESULT from students f ;

167
Ordering Data using the MAP method –
Map method will be used when the data of a user defined column needs to be sorted.

 Instances of an object type have no predefined order


 To put them in order, use a MAP method
 A MAP method is a parameter less function with a scalar return type of DATE,
NUMBER, VARCHAR2, or an ANSI SQL type such as CHARACTER or REAL
 A MAP method maps object values into scalar values (which are easier to
compare), then compares the scalar values
Example –

Step 1 -- Create the type specification –

create or replace type company as object


(city varchar2(20),
sales number,
MAP MEMBER FUNCTION sorting return number);

Step 2 – Create the type body in which sorting is mentioned. ---

create or replace type body company as


MAP MEMBER FUNCTION sorting return number is
begin
return SELF.sales;
end;
end;

Step 3 – Create the relational table

create table tata_motors


(year varchar2(30),
details company);

Step 4 – Insert records

insert into tata_motors


values('1999',company('pune',900));

insert into tata_motors


values('2000',company('mumbai',600));

Step 6 – Due to the use of MAP method we can now sort the Details column due to
which the sales figures will get sorted.
select * from tata_motors
order by details;

168
Topic 18. VARRAY Index
 Also known as varying arrays
 It is an aggregation of values stored in a single column
 A single column in the database would point to a group of elements
 Can associate a single identifier with an entire collection
 Can reference the entire collection as a whole or access the elements individually
 To access individual elements, use standard subscripting syntax i.e.
array_name(subscript)
 Maximum size must be specified at time of definition
 Index has a fixed lower bound of 1 and an extensible upper bound
 A varying array allows to store repeating attributes of a record in a single
row.

Steps for Creating VARRAYs


 Create a type which will hold information for a single line item
 Create an array of type known as Varray type, holding multiple values of a
particular type
 Create a table having one of the columns, known as varray column, based on the
Varray type

The SYNTAX for creating a VARRAY

CREATE TYPE <varray_type_name>


AS VARRAY(limit) OF <type>;

Example – In a factory table there are workers using different tools. Each worker is
using say 3 numbers of tools. Thus if a single worker is taking 3 tools then that worker’s
name will come thrice in the table. So there will be data redundancy. Varrays will repeat
values that change for a particular column which will save the storage space.

A set of rows is repeated in one column in varray.


Step 1 – Create an varray holding 3 elements of varchar2(20).

create or replace type TOOLS_VA as varray(3) of varchar2(20);

Step 2 - Create a table having one of the column as the varray column
based on the varray type.

create table factory


(name varchar2(20) primary key,
tools TOOLS_VA);

169
Step 3 – Inserting record in the factory table

Insert into factory values ('John', TOOLS_VA ('Hammer', 'Sledge', 'Drill'));


Insert into factory values ('Smith', TOOLS_VA ('Screw Gauge', 'Hammer', 'AX'));
Insert into factory values ('Martin', TOOLS_VA (null, 'Sledge', null));

To see names of tools for each worker.

select F.Name, V.*


from Factory F, TABLE(F.Tools) V;

V is an alias table created for the tool names. TABLE function is used to simplify the
process of selecting data from varying arrays.

Conditional records

To see records of persons having Hammer tool

select F.Name, V.*


from Factory F, TABLE(F.Tools) V
where V.Column_Value = 'Hammer'

Summary records

To see count of tools for each person

select F.Name, Count(Column_Value)


from Factory F, TABLE(F.Tools) V
Group By F.Name

Updating VARRAYs
 Individual elements cannot be updated
 Entire VARRAY has to be updated if one of the elements has to be changed

To change the value of first index tool of Martin from null to Vernier C

update factory
set tools = tools_va('Vernier C','Sledge', NULL)
where name = 'Martin';

170
Creating customized and advance report using PL/SQL blocks

Displaying data from the varray column –


declare
cursor cf is
select * from factory;
vcf cf%rowtype;
begin
for vcf in cf
loop
/*This loop is for the normal column name.*/
dbms_output.put_line('Contact Name '|| vcf.name);
for i in 1..vcf.tools.count
loop
/*This loop is for the number of tools for the current
row's name*/
dbms_output.put_line('-------------' || vcf.tools(i));
end loop;
dbms_output.put_line('----------------------------------');
end loop;
end;
/
------------------------------------------------------------------------------------------------------
Displaying status for each record whether Hammer is given or not—
declare
cursor cf is
select * from factory;
vcf cf%rowtype;
x number;
begin
for vcf in cf
loop
x := 0;
dbms_output.put_line('Contact Name -- '|| vcf.name);
for i in 1..vcf.tools.count
loop
if vcf.tools(i) = 'Hammer' then
x := x + 1;
end if;
end loop;
if x > 0 then
dbms_output.put_line('Hammer is supplied');
else
dbms_output.put_line('Hammer is not supplied');
end if;
dbms_output.put_line('--------------------------- ');

171
end loop;
end;
/

To display the second tool provided for each person


declare
cursor cf is
select * from factory;
vcf cf%rowtype;
x number;
begin
for vcf in cf
loop
x := 0;
dbms_output.put_line('Contact Name -- '|| vcf.name);
for i in 1..vcf.tools.count
loop
if i = 2 then
dbms_output.put_line(vcf.tools(i));
end if;
end loop;
x := x + 1;
end loop;
end;
/

Displaying the names of workers who have been given shafts.


declare
cursor cf is
select * from factory;
vcf cf%rowtype;
countOfShaftHolders number;
begin
dbms_output.put_line('The Shaft holders are --> ');
countOfShaftHolders := 0;
for vcf in cf
loop
for i in 1..vcf.tools.count
loop
if vcf.tools(i)='Shaft' then
dbms_output.put_line(vcf.name);
countOfShaftHolders := countOfShaftHolders + 1;
end if;
end loop;

172
end loop;
if countOfShaftHolders = 0 then
dbms_output.put_line('Sorry, there are no shaft holders');
end if;

end;

173
Creating a varray from a type

Multiple attributes for a single record and multiple sets of attributes for a single record.

create or replace type Monthly_Sales as Object


(Month Varchar2(3),
Sales Number(6));
/

create or replace type Monthly_Sales_VA as varray(4) of Monthly_Sales;


/

create table city_details


(name varchar2(15),
performance Monthly_Sales_VA)
/

Insert into city_details


values('Pune',
Monthly_Sales_VA(Monthly_Sales('Jan',9000),
Monthly_Sales('Feb',7500),
Monthly_Sales('Mar',8000),
Monthly_Sales('Apr',10000))
)
/

select A.name, V.*


from city_details A, TABLE(A.Performance) V;

To see details of varray query the view user_coll_types

select type_name,COLL_TYPE, UPPER_BOUND


from user_coll_types

174
Topic 19. Nested Table Index

 Table within a table


 A table is represented as a column within another table
 There is no limit to the number of rows in the nested table for each
row in the main table.
 Basically used for mapping master-detail relationships between tables.
i.e. In the parent table there would be one column, which will store the location of
the nested table.

Steps for Creating Nested Tables


 Create a type which will hold information for a single line item (this
represents the nested table details)
 Create Type of Table to hold details for the multiple rows of the nested
table (this will be the nested details - nested table type)
 Create the master table with one of the columns which will hold the nested
table type
Creating a Nested Table
SYNTAX
CREATE TYPE <nested_table_type_name>
AS TABLE OF <typename>;

Example --
There is main table Fare_Tab1.
It has columns such as Route_Coe,Route_Desc,Origin,Destination, Firts_Fare,
Bus_Fare,Eco_Fare and journey_hrs.
There is a type as table Fs_Nst_Type.
This type table holds column such as Flightno, Airbusno,Deprt_Time,
Flight_Day1 and Flight_Day2.

Now we are trying to establish the relation between Fare_Tab1 table and this
type table Fs_Nst_Type.

One route_code from Fare_Tab1 table can hold multiple Flightno,


Airbusno,Deprt_Time, Flight_Day1 and Flight_Day2. For this purpose we will
create a column in Fare_Tab1 table which will show details of the Fs_Nst_Type
for a particular route_code. So that column actually represents the nested table.

175
Step 1 -- Create a type Flight_Sch_type to hold details for a single flight.

Create or replace type Flight_Sch_Type as OBJECT


(Flightno char(4),
Airbusno char(4),
Deprt_time char(6),
Flight_day1 number(1),
Flight_day2 number(1));

Step 2 -- Create a type of table which will hold the multiple rows of the
nested table.

Create type Fs_Nst_Type


as TABLE of Flight_Sch_Type;

Step3 -- Create the main table which will have the nested table through a
column of the main table

Create table Fare_Tab


(
Route_Code char(7),
Route_Desc varchar2(30),
Origin varchar2(15),
Destination Varchar2(15),
First_fare number(5),
Bus_Fare number(5),
Eco_Fare number(5),
Journey_Hrs char(5),
Flight_Sch_Det Fs_Nst_Type
)
Nested Table Flight_Sch_Det store as Fs_Store_Tab;

Fs_Store_Tab is system generated table for oracle's reference.

Inserting records in the nested table through the main table

 SYNTAX
INSERT INTO <master_table_name>
VALUES(<master_table_column1_value>,…
<master_table_columnN_value,
nested_table_type(nested_column(attribute1_value>,...<attributeN_value>),
nested_column(attribute1_value>,…
<attributeN_value>)));

176
Insert into Fare_Tab
Values ('Goa-Ban', 'Goa - Bangalore', 'Goa', 'Bangalore', 450, 300, 200, 3,
Fs_Nst_Type ( Flight_Sch_Type('F2', 'AB01',' 9:00',1,3),
Flight_Sch_Type('F3','AB02', '11:00', 3, 5) ) );

Here in this insert statement for one route_code 'Goa-Ban' 2 nested rows of F2
and F3 are inserted.

Displaying the data of the nested table columns --

 SYNTAX in Oracle 8i
SELECT <master_table_columns>,
<nested_alias>.<attributes_from_type>
FROM THE (SELECT <nested_column>
FROM <master_table>
WHERE <master_table_condition>) <nested_alias>, <master_table>
WHERE <nested_table_condition>;

To display the nested row for the route code 'Goa-Ban' for flighno F1

Select NT.flight_day1, NT.flight_day2 ,Nt.Deprt_Time, route_code


from THE (Select Flight_Sch_Det from
Fare_Tab1
where route_code = 'Goa-Ban')
NT, Fare_Tab1
where NT.Flightno = 'F2';

In Oracle 9i Table function is used instead of THE clause which makes the
query very easy to understand

select route_code, NT.flight_day1, Nt.flight_day2,NT.Deprt_Time,NT.flightno


from Fare_Tab, Table(Fare_Tab.Flight_Sch_Det) NT
where route_code = 'Goa-Ban' And NT.flightno = 'F3'

So Syntax in 9i is
Select master_table_cols, Alias.Neste_Table_Cols
From Master_Table, Table(Master_Table.Nested_Table) Alias

To see all attribute columns of the Nested Table we can use * with alias name of
the nested table –
select route_code, NT.*
from Fare_Tab1, Table(Fare_Tab1.Flight_Sch_Det) NT

177
where route_code = 'Goa-Ban' And NT.flightno = 'F3'

Updating the nested row in 8i

 SYNTAX
UPDATE THE (SELECT <nested_column>
FROM <master_table>
WHERE <master_table_condition>) <nested_alias>
SET <nested_alias>.<attribute1_name> = <value>,...,
WHERE <nested_table_condition>;

To change the value of flight_day1 from 3 to 5 for the Flightno F3 on the


route_code 'Goa-Ban'

Update THE
(Select Flight_Sch_Det
from Fare_Tab1 F
where F.route_code = 'Goa-Ban')
NT
Set
NT.Flight_day1 = 5
where NT.Flightno = 'F3';

F is the alias name for the master table Fare_Tab


NT is the alias name for the nested table.

In 9i the same Update statement will be as follows –

To change the Flightno of the nested table to F11

update TABLE(select FLIGHT_SCH_DET


from Fare_Tab
where route_code = 'Goa-Ban') NT
Set NT.Flightno = 'F11'
where NT.Flight_Day2 = 5;

Syntax in 9i –

Update TABLE(select nested_table_name)


From Master_Table
Where master_table condition) Alias for Nested_Table
Set Alis.Nested_Col = <new value>
Where Alias.Nested_Col Condition;

178
Deleting a row from the nested table

 SYNTAX in 9i
DELETE FROM THE
(SELECT <nested_column>
FROM <master_table>
WHERE <master_table_condition>) <nested_alias>
WHERE <nested_table_condition>;

To delete the details of flightno 'F2' for the route_code 'Goa-Ban'

Delete From THE


(Select Flight_Sch_Det
from Fare_Tab1 F
where F.Route_Code = 'Goa-Ban')
NT
where NT.Flightno = 'F2';

Deleting a row in 9i –
To delete a nested row of Flight_day2 having value 5 for a route_code
‘Goa-Ban’

Delete Table(select FLIGHT_SCH_DET


from Fare_Tab
where route_code = 'Goa-Ban') NT
where NT.Flight_Day2 = 5;

Inserting a nested row to route_code ‘Goa-Ban’ – (9i)

Insert into TABLE (Select FLIGHT_SCH_DET


from Fare_Tab
where route_code = 'Goa-Ban')
Values
(Flight_Sch_Type ('F22','AB02','3',3,5))

To see number of records in the nested table

select count(*) from Fare_Tab, Table(Fare_Tab.Flight_Sch_Det) NT;

179
When to use Nested Tables instead of VARRAYs

 When the order of the data elements is not important


 When indexing is required on an attribute of the nested table type
 When there is no set limit to the number of entries for the data elements
 When the data elements need to be queried

180
ASSIGNMENTS
1. Programming Basics Index
1) Write a plsql block that displays details of a given employee. Prompt the user to enter
the empno

2) Write a plsql block to accept the empno from the user. Raise the salary of that
employee by 20% and display the raised salary. (Note - Do not update the salary, just
display the changed salary)

3) Accept job type from the user. Display the message depending upon whether
no rows or one row or several rows are selected.
The message should be any one from the below 3 as per the situation.
JOB TYPE FOUND ONCE
JOB TYPE FOUND MORE THAN ONCE
JOB TYPE NOT FOUND

4) Write a plsql block to display the names of the emp table in the reverse order.

5) Using basic loop technique display all the multiples of 7 between 31 and 48.

6) Write a block to accept the Empno from the user and change the salary according to
the following condition. If salary is in the range of
RANGE INCREMNENT
1000 – 2000 500
2001 –3000 1000
3001 – 4000 1500
>4000 2000
(Note - Do not update the salary, just display the changed salary)

7) Write a PL/SQL block that determine the top employees with respect to salaries.
E.g. if user enters 5 from keyboard the program should display 5 top salaried employees.

8) Create a table Inspection_Details_EmployeeID that has one column Readings of


numeric type. Using pl/sql block add numbers, which has the difference of 0.1. The
numbers should be between 0.1 and 6.8.

9) Produce the following output –

181
10) Produce the following sub-total report.

11) Display the lowest 3rd salary values from emp table.

182
12) Through while loop display the multiples of 7 till 70 in the descending
order.

13) Display the highest salary programmatically without using the max()
function. Use the Basic type of loop only.

14) Display the difference of salary between the oldest and the latest
employee.

15) Create the table tx1 with the following script.


Create table Oracle_Batch(student_name varchar (20);

Create a program that will accept the student_name form the user and
if the user has entered all the characters as alphabets only then enter that
name into the Oracle_Batch table.

16) Write a PL/SQL code to accept an employee number from the


user and display whether it is exists or not .

17) Write a PL/SQL code to display the name, salary and grade of
the employee by accepting employee code. Grade is ‘A’ if salary
>25000, ‘B’ if salary > 15000, ‘C’ for other salaries. Use CASE
statement only.

183
2. Cursor Index

Using the cursor mechanism write the following PLSQL blocks.

1) Check whether the 3rd Clerk’s salary is greater than the 2nd Clerk’s salary. If it is so,
then display message as “Third has more salary than the second” otherwise display
message as “Second has more salary than the first”

2) Display the sum of first five salaries from the emp table.

3) The management has decided to increase salary of employees by 10%. Starting with
lowest paid earner and working up. If at any time salary bill exceeds 45000/- then no
further employees are to be given an increase.

4) Display the names of the employees who are earning salary less than the average
salary of their own jobs.

5) Use a parameterized cursor which will take deptno as the parameter and will display
the highest two salaries of that deptno.

7) Create the table Emp_Coupons with the two fields Name and Coupon_No
Enter the following records in it.

John 80
Martin 83
Allen 87
Roger 78
Adams 88
Kim 89

Make a PLSQL block that will check whether the coupon number of the current record is
greater than the previous. If any record’s coupon number is less than the previous one
then display the name of the person whose coupon number is less.

8) Display the name, sal and the cumulative total of salaries.


Cumulative salary would be first salary +0 for the first record, second + first for the
second and so on.

9) Comparing the total of salaries of the even number records and the odd number
records from the emp table and display the highest total.

10) Check the total of salaries of the first 3 employees. If that total is greater than or
equal to 3500 then increment those first 3 employees salaries by 15% else do not
increment the salary.

184
3. PLSQL Table Index
1) Add multiples of 5 from 5 to 130 in a plsql table and display them from the plsql
table in the descending order.

2) Using a cursor add the rows of all SALESMAN in the plsql table and then
display only the last row of that plsql table

3) Create a table Tolerance _Limits that will have two columns Tolerance_No and
Tolerance_value.
Use a plsql block to have values from 40.0, 40.1, 40.2 till 57.7
Through a plsql block add the records in the table Tolerance _Limits which will
have numbers starting from 1 in Tolerance_No column and each value
from the plsql table.

4) Take all the salaries from the emp table in a plsql block using a cursor. Then
display only those salaries from the plsql block which have value >= 2800.

5) Take the row of SMITH from the mp table. Enter the entire row in a column of a
PLSQL table and display the PLSQL table.

6) Accept the job type from the user. Populate a plsql table with all the employee
names within that job type. Display the names of those employees along with
count of employees of that job type.

185
4. Exception Handling Index

1. Accept the name of the employee. Display the salary of that


employee.
Handle all possible run-time errors.

2. Create the ex_tab table with the following specifications:

Name Data type Constraint


A Integer Primary key
B Integer Unique
C Char(3) Not Null
D Integer Must be >= 10
E Integer Foreign key from A
column

For trapping all the constraint violation errors create another table
error_log that has 3 columns Record_Number, Error_Number and
Error_Message.
Through a plsql block accept all the 5 column values from the user
run-time, if any error violation takes place then the error_log table
should get the record displaying the details of that error.
If there is no error then the record should get added in the ex_tab
table.

3. Create a table Emp_Details which has the following specifications.

Name Data type Constraint


Empno Integer Primary Key
Membership_no Char Unique
Name Varchar Not Null
Salary Integer Above 10000
Mgr Integer Refers to the Empno column

Take all the column values from the user and enter the record in
the table Emp_Details. If any constraint violation happens then
provide user friendly error message according to the error.

186
4. Increase the salary of employees for dept no 30 by 20% . Create an
exception missing_salary which will show you an alert message for
those having NULL value in the salary field .

5. Run the following script.


create table mm1
(empid number, ename varchar2(10), job varchar2(10));
insert into mm1 values(1,'John','Manager');
insert into mm1 values(2,'Martin','Clerk');
insert into mm1 values(3,'Smith','Clerk');
Create a plsql block which will accept empid, ename and job
from the user. If the complete record is duplicated then raise an exception
FOUND_DUPLICATE_RECORD. If the record in not duplicate then add
it into the mm1 table.

6. Create a table Customer_Data which has 5 fields like Custid, Qty,


Required_In_Days, Qty_Per_Day and Rate_Per_Day.
Take the first 3 columns values from the user.
You want to calculate Quantity per day which would be qty given divided by
Required_In_Days.
Once Quantity per day is calculated do the calculation of Rate_Per_Day which will
be Quantity per day * 100.
Finally when all the values are ready add a new record in the table Customer_Data.
Handle all types of error that may occur at different point of times.

187
5. Procedures Index

1. Create a procedure named Job_Details, which will show the total, highest, lowest
and average of the salary for the job. (Job type is the parameter to be passed.)

2. Create a procedure named Change_Salary that will accept salary and will return
the 40% raised salary.

3. Create a table Emp_Proc having structure and records same as that of emp table.
Create a procedure Delete_Employee that will take department name as a parameter.
The procedure should delete the record of the employees from the Emp_EmployeeID
table only for the department name passed.

4. Create a procedure, NEW_EMP to insert a new employee into the Emp_Proc


table . The procedure should ensure whether the department ID specified for the new
employee exists in the DEPARTMENTS table.

5. Create a table Company_Data with columns empid, ename and job. There is no
primary key or unique key constraint to the empid column.
Create a procedure Add_Company_Data which will take empid as the parameter
and adds a new record in that table if the empid with the same value does not exist.

6. Create a table Emp_Sal that will have 2 columns – Empno and Gross_Salary
Create a procedure Salary_Details which will take Empno,Basic salary and
Allowance as the three parameters from the user.
There has to be a sub procedure Tax_Calculations that will calculate the tax value
as per the salary values given.
There is a small chart for tax calculations.

Basic Tax
Above or equal to 5000 20% of the basic
Between 4000 and 4999 15% of the basic
Between 3000 and 3999 10% of basic and 2% of Allowance
Between 2000 and 2999 5% of basic and 1% of Allowance
Less than 2000 100

Once the tax value is calculated the Gross calculation will be done as ( Basic
+Allowance ) – Tax.
After calculating the tax main procedure will add the record in the Emp_sal table.

7. Considering the emp table create a procedure IsHighest that will take the ename as
the parameter. The procedure should reply whether that employee is the highest
earner or not.

188
6. FUNCTIONS Index
4. Create a table student. It has four fields rollno, name, marks and grade.
Note – Grade column has be generated by a function get_Grade(). This function
takes marks as argument and generates the grade as per the following table

Marks Criteria Grade


Greater than or equal to 70 Distinction
Between 60 and 69 First Class
Between 50 and 59 Second Class
Less than 50 Failed.

When the record is inserted then the grade value should be generated.

5. Create a function Raise_Sal that will accept salary as a parameter and returns 15%
raised salary. Use this function in the select statement of the emp table to see the
result.

6. Create a function Yearly_Raise that will take the salary, deptno and job as the
parameters and raise the salary according to different criteria.
Criteria Raise
Clerk employees of deptno 20 earning 20%
salary above 1000
Clerk employees of deptno 20 earning 15%
salary less 1000
Clerk employees of deptno 20 earning 25%
salary above 1000
Clerk employees of deptno 20 earning 18%
salary less than 1000
Clerk employees of deptno 30 having any 10%
salary

Use this function to update salaries of the employees of job Clerk in the table emp.

4. Run the following script.


Create Table Company_Product (ProductId Varchar(20));

Create a function check_productid that will take a productid (string) as a parameter.


The function should check the last 4 characters of that productid. If they are KPIT
(only upper case) then a new record must get created in the table Company_Product
else throw a relevant error message.

189
5. Considering the emp table create a function Last_Employee which will take the job
type as the parameter and display the last employee (s) joined in that job.

6. Considering the emp table create a function TopN_Salary that will take the top Nth
number as the parameter and returns the highest salary at that position.

190
7. Packages Index

7. Create a package Clerk_Salary. This package should have the following members.
a. Basic has a fixed value of 10000. Only package members should be able
to use the basic figure.
b. Function get_HRA(). It should be 40% of basic.
c. Function get_DA(). It should be 35% of basic.
d. Function get_TA(). It should be 10% of HRA.
e. Function get_PF(). It should be 15% of (basic+hra+da+ta)
f. Function get_TAX(). It should be a fixed value 7500.
g. Function get_Gross(). It should be basic+hra+da+ta – (pf + tax).
h. Procedure display_Gross. It should show the gross figure.
Execute the display_Gross procedure and display the gross amount.

8. Create a package OverLoaded_Total that has function Show_total. This function


takes salary and/or commission as parameters and returns the total as:
i. If only salary was passed then the total should be 30% raised
salary.
ii. If salary and commission were passed then the total should be
salary+commission.

------------------------------------------------------------------------------------------------

3. Customer - Order Processing Package

Create the following Tables:

Customer

Cust_no Number
Cust_Name Char
City Char
Tel_No Char

Order

Order_No Number
Cust_No Number
Amount Number
Status Char(1) (P-Pending , D – Delivered)

191
Item

Item_No Number
Order_No Number
Qty Number
Rate Number

Add 4 sample records in each table.

Write a package containing suitable cursor, variables, exceptions etc to perform following
tasks:

1) Function to check valid Order_No for item table, a valid order_no is one which
already exist in order table.
2) Procedure to update the amount for a particular order_no in the order table when a
new item is added in Item table. Accept the values for adding new record to item
table as parameters to the procedure.
3) Function to accept the order no that returns the status of the order.
4) Function to accept the customer no and return the total of all the amounts (for all
the orders given by that customer)
5) Procedure to display the summary for all the records in the order table as follows.
Total no of orders –
No of pending orders –
No of delivered orders

4. Create a package Emp_Package that will have a plsql table called Emp_Info containing
Enames and Sal from the emp table.
The package has two procedure Show_Clerks and Show_Managers which will display
the enames and salaries of the Clerks and Managers respectively. These procedures
should take the names and salaries from the Emp_Info plsql table only.
The Emp_Info always should get loaded within the session first when any of the package
member is getting called for the first time within that session.

192
8. Triggers Index

The following triggers have to be created on the emp and dept tables –

9. Create a trigger No_DML_in_Lunch, which will not allow records to be entered


between 1 and 2 pm on weekdays.

10. Create a trigger DML_on_View which will allow insert, update and delete for a
view which is based on an inner join between tables Emp and Dept

11. Create a trigger ClerkAndAnalystOnly, which will allow records getting inserted
in the table emp if the job of the employee is CLERK and ANALYST only.

4. Create a table Emp_Summary that has 4 columns Record number, Lowest_Sal


Highest_Sal and Average_Sal. (Record_no is the auto generated number). Create
a trigger Emp_Stats that will add a new record into the table Emp_Summary,
which will get the auto record number, lowest, highest and the average salaries of
the emp table, whenever a new record is added in the emp table or whenever any
record is updated.

5. Create a table New_Top_employees that will have empno,ename,sal as the


columns. Create a trigger Check_New_Top that will get fired whenever a new
employee record is added in the emp table. If the new record’s salary is greater
than or equal to 2500 then only that record’s empno, name and salary should be
transferred into the New_Top_employees table.

193
9. Dynamic SQL Index

1. Create a procedure Create_Table that will take the table name as the parameter
and create a table with KPIT_ as the prefix for the table name which is given as
the parameter.
The table should have columns as KPNo, Name and BU

2. Create a procedure Drop_Table that will take the table name as the parameter. The
procedure should only drop the table if the Number of rows within that table are
less than 10.

3. Create a procedure Alter_table that will take table name, column name and data
type as 3 parameters. The column name is the column whose data type has to be
changed and the data type is the new data type to be given to that column.
The procedure should only change the data type if that column is empty, else
appropriate error message has to be flashed.

194
10.Varray Assignment Index

Create a varray Shift_Data that has values of maximum 3 shifts.

Create a table production that has the following columns:

Name Data type


Production_id Integer
P_Date Date
Shift Shift_Data (Varray)

Enter two records in the production tables with each record having the 3 values for the
shifts.

195
11. Nested Table Index
Create a type match_details that has the following attributes:

Name Data type


MId Integer
M_date Date
Opponent_team Varchar2
Tickets_Sold Integer

Create a nested table type match_details_nst based on the above type.

Create a table Ground_Data that has the following columns:

Name Data type


G_Id Integer
G_Name Varchar2
Area Number
Match_Data match_details_nst (Nested Table)

Enter two records in the Ground_data table with 2 nested records for each main record.

---------------------------- xxxxxxxxx --------------------------------

196

You might also like