You are on page 1of 80

Introduction to PL/SQL

Procedural language/SQL is oracle corporation’s procedural extensions to SQL . PL/SQL


is a database-oriented programming language that extends Oracle SQL with
procedural capabilities by adding programming constructs found in procedural
languages, resulting in a structural language that is more powerful than SQL.

PL/SQL was developed by Oracle in early 90’s to enhance the capabilities of SQL.
A procedural language allows writing a program that specifies a list of operations to be
performed sequentially to achieve the desired result.You can develop modularized
applications with database procedures using database objects such as the following :

 Procedures

 functions

 Packages

 Database triggers

Modular applications improve :

 Functionality

 Security

 Overall performance

Benefits of PL/SQL :-

Improved performance :-

Pl/sql can be used to group SQL statements together within in a single block and to
send the block to the server in a single call , thereby reducing network traffic. Without
PL/SQL the SQL statements are sent to the ORACLE server one at a time. Each SQL
statement results in another call to the ORACLE server and higher performance
overhead.

Portability:-

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

Control structures :-

Control structures allow you to do the following :-

 Execute sequence of statements conditionally.

 Execute sequence of statements iteratively in a loop.

-1-
Error handling :-

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

 Process ORACLE server errors with exception-handling routines.

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


routines.

-2-
PL/SQL ENVIRONMENT :-

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

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

PL/SQL Blocks:-

PL/SQL Blocks are two types

 Anonymous

 Named

Anonymous Block:-

A PL/SQL block has the following structure:

-3-
Comments:-

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

-- Single line comment

/* */ Multi line comment

PL/SQL Variables :-

Variables can be used for :

 Temporary storage

 Manipulation of stored values.

 Reusability

 Ease of maintance

A data variable can be declared as:

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

Where,

VariableName is the name of the variable.

CONSTANT flags that placeholder as a constant.

DATATYPE is a valid PL/SQL datatype

DEFAULT Initial Value is an optional specification that allows initializing a variable

PL/SQL Data Types:-

 Scalar datatypes

 Composite datatypes

 Reference datatypes

-4-
Scalar datatypes :-

Variable of scalar datatype holds a single value.

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

Veno NUMBER(4);

Vename VARCHAR2(20);

Vdoj DATE;

Declaring a constant

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

The following returns an error:

ProductPrice CONSTANT Numeric(8,1);

Declaring a variable with an initial value

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

Declaring a variable as NOTNULL

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

Assigning Values To Variables:-

A variable can be assigned a value by:

-5-
Using the assignment operator :=

Syntax:-

<VariableName> := <Value>;

X := 10;

Composite datatypes :-

Composite datatype allows group of elements.

 PL/SQL table

 PL/SQL record

Reference Types :-

%TYPE :-

Declaring a variable according to :-

A database column definition.

Another previously declared variable.

Example:-

Veno emp.empno%type;
Vename emp.ename%type;
Vsal emp.sal%type;
Example:-

DECLARE
vEmpno Number(4);
vEname Varchar2(20);
BEGIN
/* Using assignment operator. */
vEmpno := &Empno;
/* Using direct assignment from table column. */
SELECT ename into vEname FROM EMP
WHERE EMPNO=vEmpno;
/* Printing a message. */
DBMS_OUTPUT.PUT_LINE(‘Address: ‘|| vEname);
END;
/
DBMS_OUTPUT is a package that includes a number of procedures and functions that
accumulate information in a buffer so that it can be retrieved later. These functions can
be used to display messages.

-6-
PUT_LINE puts a piece of information in the package buffer followed by an end-of-line
marker. It can also be used to display a message. PUT_LINE expects a single
parameter of character datatype. If used to display a message, it is the message
string.

To display messages, the SERVEROUTPUT should be set to ON. SERVEROUTPUT is a


SQL Plus environment parameter that displays the information passed as a parameter
to the PUT_LINE function.

Syntax:-

SET SERVEROUTPUT [ON |OFF]

Control Statements:-

PL/SQL provides the following control structures:

Conditional Statements

Iterative Statements

Sequential Statements

Conditional Statements:-

Syntax:-

1 if then else:-

IF <Condition> THEN
STATEMENTS;
ELSE
STATEMENTS;
END IF;

2 multi if :-

IF <Condition> THEN
STATEMENTS;
ELSIF <Condition> THEN
STATEMENTS;
ELSE
STATEMENTS;
END IF;

-7-
3 nested if :-
IF <Condition> THEN
IF <Condition> THEN
STATEMENTS;
ELSE
STATEMENTS;
END IF;
END IF;
Example:-

DECLARE
vEmpno emp.empno%type;
vJob emp.job%type;
BEGIN
vEmpno := &empno;
SELECT job INTO vJob FROM EMP WHERE EMPNO=vEmpno;
IF vJob=’CLERK’ THEN
UPDATE EMP SET SAL=SAL*1.1 WHERE EMPNO=vEmpno;
ELSE vJob=’MANAGER’ THEN
UPDATE EMP SET SAL=SAL*1.2 WHERE EMPNO=vEmpno;
ELSE
UPDATE EMP SET SAL=SAL*1.05 WHERE EMPNO=vEmpno;
END IF;
END;
Iterative Statements:-

PL/SQL provides the following three types of statements

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

A Simple Loop repeats a set of statements in the PL/SQL code block at least once
before the loop terminates.

When the EXIT condition is satisfied the process exists from the loop.

Syntax:-

LOOP

<Sequence of statements>

END LOOP;

-8-
Example:-

A single column table called RandomNumbers exists. This table holds a set of random
numbers which are used by the application.Create a program using PL/SQL that:

Accepts a number which indicates the number of random number to be generated

Generates that many random numbers. Stores them in the RandomNumber table

DECLARE
varHowManyTimes Number;
varCtr Number :=0;
BEGIN
VarHowManyTimes := &HowManyNos;
LOOP
varCtr := varCtr + 1;
INSERT INTO RandomNumbers(RandNo)
VALUES (DBMS_RANDOM.RANDOM());
EXIT WHEN varCtr >= varHowManyTimes;
END LOOP;
DBMS_OUTPUT.PUT_LINE(TO_CHAR(varCtr) ||
‘random numbers generated successfully.’);
END;

WHILE Loop:-

A While Loop repeats a set of statements in the PL/SQL code block as long as a
condition is true.

The condition is evaluated when iteration begins. The iteration continues until the
condition becomes false.

Syntax:-

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

1. Initialize a variable before the loop body

2. Increment the variable in the loop

-9-
Example 1:-

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

DECLARE
D1 DATE;
D2 DATE;
CNT NUMBER := 0;
BEGIN
D1 := ‘&DATE1’;
D2 := ‘&DATE2’;
D1 := NEXT_DAY(D1-1,’SUNDAY’);
WHILE(D1<=D2)
LOOP
CNT := CNT+1;
D1 := D1+7;
END LOOP;
END;

FOR Loop:-

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


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

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

Syntax:-

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


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

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


necessary to declare it explicitly

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


explicitly

-10-
Example 1 :-

FOR I IN 1..10 FOR I IN REVERSE 1..10


LOOP LOOP
DBMS_OUTPUT.PUT_LINE(I); DBMS_OUTPUT.PUT_LINE(I);
END; END;

Example 2 :-
Write a pl/sql program to input a string and print reverse of that string ?
DECLARE
S1 VARCHAR2(20);
S2 VARCHAR2(20);
L NUMBER;
BEGIN
S1 := ‘&STRING’;
L := LENGTH(S1);
FOR I IN 1..L
LOOP
S2 := S2 || SUBSTR(S1,-I,1);
END LOOP;
DBMS_OUTPUT.PUT_LINE(‘Reverse of that string :- ‘|| S2);
END;

GOTO Statement:-

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

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

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


statement.

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

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

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

-11-
Syntax:-

GOTO <CodeBlock Name>;

ERROR HANDLING IN PL/SQL

a PL/SQL block has the following structure:

DECLARE
<VARIABLES> ;
BEGIN
EXECUTABLE STATEMENTS;
EXCEPTION
WHEN EXCEPTION_NAME THEN
ERROR – PROCESSING STATEMENTS;
END;
When an error occurs that raises a built-in exception, the exception is said to be raised
implicitly. In other words, if a program breaks an Oracle rule, control is passed to the
exception-handling section of the block. At this point, the error-processing statements
are executed. It is important for you to realize that after the exception-handling section
of the block has executed, the block terminates. Control will not return to the
executable section of the block.

In PL/SQL exceptions are two types :-

1 system defined exceptions

2 programmer defined exceptions

SYSTEM DEFINED EXCEPTIONS :-

These exceptions are defined by ORACLE.The following list explains some commonly
used predefined exceptions and how they are raised:

NO_DATA_FOUND:-

This exception is raised when a SELECT INTO statement does not return any rows.

a SELECT statement that calls a group function will never raise the NO_DATA_FOUND
exception.

TOO_MANY_ROWS:-

This exception is raised when a SELECT INTO statement returns more than one row. By
definition, a SELECT INTO can return only a single row. If a SELECT INTO statement
returns more than one row, the definition of the SELECT INTO statement is violated.
This causes the TOO_MANY_ROWS exception to be raised.

-12-
ZERO_DIVIDE:-

This exception is raised when a division operation is performed in the program and a
divisor is equal to zero.

LOGIN_DENIED:-

This exception is raised when a user is trying to login to Oracle with an invalid
username and password.

PROGRAM_ERROR:-

This exception is raised when a PL/SQL program has an internal problem.

VALUE_ERROR:-

This exception is raised when a conversion or size mismatch error occurs.

DUP_VALUE_ON_INDEX:-

This exception is raised when a program tries to store a duplicate value in the column
or columns that have a unique index defined on them.

Example :-

DECLARE
VENO EMP.EMPNO%TYPE;
VENAME EMP.ENAME%TYPE;
BEGIN
VENO := &EMPNO;
SELECT ENAME INTO VENAME
FROM EMP WHERE EMPNO=VENO;
DBMS_OUTPUT.PUT_LINE(VENAME);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE(‘NO SUCH EMPLOYEE’);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(‘UNKNOWN ERROR’);
END;

SQLCODE & SQLERRM :-

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

 SQLCODE
 SQLERRM
SQLCODE returns ERROR CODE

SQLERRM returns ERROR MESSAGE

-13-
Example :-

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

These EXCEPTIONS are defined by PROGRAMMER.

These EXCEPTIONS must be raised by PROGRAMMER by using

1 RAISE statement

2 RAISE_APPLICATION_ERROR built-in procedure.

Using RAISE statement :-

Example :-

DECLARE
X NUMBER(2);
Y NUMBER(2);
Z NUMBER(2);
ONE_DIVIDE EXCEPTION;
BEGIN
X := &X;
Y := &Y;
IF Y=1 THEN
RAISE ONE_DIVIDE;
END IF;
Z := X/Y;
DBMS_OUTPUT.PUT_LINE(Z);
EXCEPTION
WHEN ZERO_DIVIDE THEN

-14-
DBMS_OUTPUT.PUT_LINE(‘ZERO DIVIDE ERROR’);
WHEN ONE_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE(‘ONE DIVIDE ERROR’);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
Using RAISE_APPLICATION_ERROR :-

Syntax:-

RAISE_APPLICATION_ERROR(ERROR NUMBER,ERROR MSG);

ERROR NUMBER can be between -20001 and -20999.

Example :-

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

PRAGMA EXCEPTION_INIT :-

It is the way to associate user defined exception with oracle Predefined error.

PRAGMA EXCEPTION_INIT(user defined exception,oracle error);

-15-
Example :-

DECLARE
VDNO DEPT.DEPTNO%TYPE;
child_exists EXCEPTION;
PRAGMA EXCEPTION_INIT(child_exists,-2091);
BEGIN
VDNO := &DEPTNO;
DELETE FROM EMP WHERE DEPTNO=VDNO;
DBMS_OUTPUT.PUT_LINE(‘RECORD DELETED SUCCESSFULLY’);
EXCEPTION
WHEN child_exists THEN
DBMS_OUTPUT.PUT_LINE(‘child records present’);
END;

After executing above program any statement causes -2091 error then ORACLE raises
child_exists exception.

-16-
Cursors :-
Oracle allocates a certain portion in the memory, for every SQL query that is
executed. Using PL/SQL, this portion in the memory can be given a name of choice.
This portion is called the context area or a Cursor.

A cursor is basically called a pointer to the context area and thus represents a
structure in memory. Using a PL/SQL program, this context area can be controlled
using the cursor.

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

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

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

Types Of Cursors:-

Oracle provides the following two kinds of cursors.

 Explicit Cursor

 Implicit Cursor

Explicit Cursor:-

A cursor that is explicitly opened for processing data using a PL/SQL block is known as
an Explicit Cursor. An explicit cursor is useful, when it is required to process individual
records (row-by-row) from a database table. An Explicit Cursor is declared in the
DECLARE section of a PL/SQL program.

Explicit cursors are used in queries that return multiple rows. Processing multiple rows
is very similar to processing a flat file.

A flat file can be processed as:

 Open a file
 Process records
 Close the file

-17-
Similarly, an explicit cursor can be processed as:

 Declare a cursor mapped to an SQL SELECT query


 Open the cursor
 Fetch records/ rows from the cursor one row at a time into memory
variables
 Process the data held in the memory variables as required using a loop
 Exit from the loop after processing is complete
 Close the cursor
Now that the cursor is declared, the next logic step is to use it. A cursor can be put to
use, by opening, fetching the data and finally closing it when not required.

Declaring Cursor:-

CURSOR <CursorName> IS <SELECT Query>

Example:-

DECLARE

CURSOR curEmp IS SELECT ename,sal FROM emp ;

BEGIN

END;

Opening Cursor:-

A cursor after it is declared needs to be opened.

Syntax:-

OPEN <CursorName>;

Example:-

OPEN C1;

The following events occur when cursor is opened :-

 Defines a named portion in the memory


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

Fetching Data From Cursor:-

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

-18-
Syntax:-

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

Example :-

FETCH c1 INTO varEname,varSal;

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

Closing Cursor:-

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

Syntax:-

CLOSE<CursorName>;

Example :-

CLOSE C1;

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

Cursor Attributes:-

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

%FOUND:-

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


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

%NOTFOUND:-

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

%ROWCOUNT:-

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

-19-
%ISOPEN :-

Returns TRUE if cursor is opened otherwise returns FALSE.

Example :-

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

Using simple loop:-

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

Using While Loop:-

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

-20-
Cursor FOR Loop:-

Cursor also allows using a FOR Loop which helps processing multiple records.

A FOR Loop implicitly:

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


 Opens a cursor
 fetches records
 Closes the cursor automatically when all records have been
processed
Hence the commands such as OPEN, FETCH and CLOSE are not required here.

Syntax:-

FOR <Variable> IN <CursorName>


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

Example:-

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

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

Generates Username and Password for each employee that is fetched

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

The username should be formed as:

FirstName LastName EmployeeNo Username

-21-
Sharanam Shah 1 AMSH1

The password should be formed as:

FirstName LastName Salary Password

Sharanam Shah 35232 SHSH32

DECLARE
CURSOR curEmp IS
SELECT EmployeeNo, FirstName, LastName, Salary FROM
Employees;
varUname Employees.Username%TYPE;
varPwd Employees.Password%TYPE;
BEGIN
FOR r IN curEmp
LOOP
varUname :=SUBSTR(r.FirstName, -2, 2)
||SUBSTR(r.LastName, 1, 2)|| r.EmployeesNo;
varPwd :=SUBSTR(r.FirstName, 1, 2)
||SUBSTR(r.LastName, 1, 2)
||SUBSTR(TO_CHAR(r.Salary), -2, 2);
UPDATE Employees SET Username = varUname,
Password = varPwd
WHERE EmployeeNo = r.EmployeeNo;
END LOOP;
COMMIT;
END;

Defining An Inline Cursor:-

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

Example:-

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

-22-
END;

Parameterized Cursor:-

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

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

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


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

Declaring Cursor:-

Syntax:-

CURSOR <CursorName> (VariableName> <Datatype> IS

<SELECT statement . . . . >

Opening Cursor:-

Syntax:-

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

FOR <variableName>

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

Example:-

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

Cursor Within Cursor:-

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

-23-
Write a program using PL/SQL that:

Fetches departments from the Departments table

For every department, fetches employees belonging to that department from the
Employees table and Add all the information from both the tables to the EmpDept
table

DECLARE
CURSOR c1 IS SELECT * FROM Dept;
CURSOR c2(d Dept.DeptNo%TYPE)
IS SELECT * FROM Emp WHERE DeptNo =d;
BEGIN
FOR d IN c1
LOOP
FOR e IN c2(d.deptno)
LOOP
INSERT INTO EmpDept(EmpNo,Ename,Sal,Deptno,Dname,Loc)
VALUES (e.EmpNo,e.Ename,e.Sal,d1.Deptno,d1.Dname,d1.Loc)
END LOOP;
END LOOP;
COMMIT;
END;

Locking Cursor Data:-

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

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

Syntax :-

CURSOR C1 IS SELECT * FROM EMP FOR UPDATE;

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

-24-
CURSOR C1 IS SELECT * FROM EMP FOR UPDATE OF SAL NOWAIT;

Example :-

DECLARE
CURSOR c1 IS SELECT * FROM EMP
FOR UPDATE OF SAL;
BEGIN
FOR e IN C1
LOOP
UPDATE EMP SET SAL=SAL*1.1 WHERE EMPNO=e.EMPNO;
END LOOP;
COMMIT;
END;
Example:-

T1

F1 F2 F3
5 4 F1+F2
5 4 F1-F2
5 4 F1*F2
5 4 F1/F2
Write a pl/sql program to update F3 as give above.

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

-25-
CURSOR Variables (REF cursor):-

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

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

There are two types of cursor variable.

1 WEAK

2 STRONG

Weak REF Cursor:-

A REF CURSOR declared without RETURN type is called WEAK REF CURSOR.

PL/SQL provides a built-in WEAK REF CURSOR called SYS_REFCURSOR.

Example :-

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

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

To create a cursor variable

1 REF CURSOR type and then

2 Declare a variable of that type.

Declaring a REF CURSOR Type:-

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

TYPE REFTYPE IS REF CURSOR RETURN EMP%ROWTYPE;

-26-
Declaring a variable of REF CURSOR TYPE :-

Variablename datatype ;

Example :-

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

Implicit cursor:-

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

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

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

 Reserving a portion of memory

 Populating this portion with appropriate data

 Processing the data in the memory area

 Releasing the memory area when the processing is complete

 it is passed to the client who demanded it.

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

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

-27-
Attributes:-

%FOUND :-

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

%NOTFOUND :-

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

%ROWCOUNT :-

Returns no of records affected by the last SQL statement.

Example:-

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

Write a program using PL/SQL that:

Accepts the EmployeeNo of the employee to be transferred

Accepts the new DeptNo of that employee

Updates the Department number of that employee to the new DeptNo

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

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

Collections :-
-28-
A collection a is a group of elements of the same datatype.Each element is identified
by a unique subscript that represents its position in the collection.

There are two types of collection datatypes in PL/SQL

1 table

2 varray

PL/SQL TABLE :-

A PL/SQL table is similar to one-column database table.Each element is assigned a


consecutive subscript starting at 1.

To use PL/SQL table

 Declare pl/sql table type.

 Declare a variable of pl/sql table type.

Declaring PL/SQL table type :-

TYPE <TYPE_NAME> IS TABLE OF <ELEMENT_TYPE> INDEX BY BINARY_INTEGER;

Example:-

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

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

Declaring a Variable of PL/SQL table type:-

TABLE_NAME TYPE_NAME ;

Example:-

X arrtype ;

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

Example :-

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

-29-
DBMS_OUTPUT.PUT_LINE(X(I));
END LOOP;
END;

Collection Methods :-

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

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

EXTEND :- increases the size of a collection.

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


element from a collection.

FIRST :- returns subscript of a FIRST element.

LAST :- returns subscript of a LAST element.

PRIOR :- returns subscript that precedes a specified collection subscript

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

These methods must be accessed with collection name.

Bulk Collect Operation:-

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

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

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

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

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

Example:-

DECLARE
TYPE etype IS TABLE OF varchar2(20) INDEX BY BINARY_INTEGER;
e etype;
BEGIN
SELECT ENAME BULK COLLECT INTO e FROM EMP;
FOR X IN e.FIRST.e.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(e(X));
END LOOP;
END;

Note :- prior to oracle 9i R2 we can bulk collect only single column values, from 9i R2
we can also bulk collect records .

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

DECLARE
TYPE etype IS TABLE OF EMP%ROWTYPE INDEX BY BINARY_INTEGER;
E etype;
X number;
BEGIN
SELECT * BULK COLLECT INTO E FROM EMP;
X := E.FIRST;

WHILE(X <= E.LAST)

-31-
LOOP
DBMS_OUTPUT.PUT_LINE(E(X).EMPNO||’ ‘||E(X).ENAME||’ ‘||E(X).SAL);
X := E.NEXT(X);
END LOOP;
END;
A PL/SQL program to display all employee records (backward navigation):-

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

BULK BIND using FORALL loop :-

The FORALL syntax allows us to bind the contents of a collection to a single DML
statement, allowing the DML to be run for each row in the collection without requiring a
context switch each time.

EMP_TEMP
EMPNO ENAME SAL

Using FORALL and BULK COLLECT copy data from EMP TO EMP_TEMP

DECLARE
type empno_array is table of emp.empno%type
index by binary_integer;
type ename_array is table of emp.ename%type
index by binary_integer;
type sal_array is table of emp.sal%type
index by binary_integer;

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

SQL%BULK_ROWCOUNT:-
The SQL%BULK_ROWCOUNT cursor attribute gives information about the rows
affected by each iteration of the FORALL statement.
DECLARE
TYPE etype IS TABLE OF number(2);
e etype := etype(10,20,30,40);
BEGIN
/* PERFORM BULK DELETE OPERATION */
FORALL i IN e.FIRST..e.LAST
DELETE FROM emp WHERE deptno = e(i);
/* ROWS AFFECTED */
FOR i IN e.FIRST..e.LAST

-33-
LOOP
DBMS_OUTPUT.PUT_LINE(‘ Deptno = ‘||e(i)||’ ‘||
’Rows affected = ‘||SQL%BULK_ROWCOUNT(i));
END LOOP;
END;

OUTPUT :-
Deptno =10 Rows affected = 3
Deptno = 20 Rows affected = 5
Deptno = 30 Rows affected = 6
Deptno = 40 Rows affected = 0

SAVE EXCEPTIONS and SQL%BULK_EXCEPTION :-

We saw how the FORALL syntax allows us to perform bulk DML operations, but what
happens if one of those individual operations results in an exception? If there is no
exception handler, all the work done by the current bulk operation is rolled back. If
there is an exception handler, the work done prior to the exception is kept, but no
more processing is done. Neither of these situations is very satisfactory, so instead we
should use the SAVE EXCEPTIONS clause to capture the exceptions and allow us to
continue past them. We can subsequently look at the exceptions by referencing the
SQL%BULK_EXCEPTION cursor attribute.

To see this in action create the following table :-

SQL>CREATE TABLE exception_test ( id NUMBER(10) NOT NULL);

The following code creates a collection with 100 rows, but sets the value of rows 50
and 51 to NULL. Since the above table does not allow nulls, these rows will result in an
exception. The SAVE EXCEPTIONS clause allows the bulk operation to continue past
any exceptions, but if any exceptions were raised in the whole operation, it will jump to
the exception handler once the operation is complete. In this case, the exception
handler just loops through the SQL%BULK_EXCEPTION cursor attribute to see what
errors occured.

DECLARE
TYPE t_tab IS TABLE OF exception_test.ID%TYPE;
l_tab t_tab := t_tab();
l_error_count NUMBER;

-34-
BEGIN
-- Fill the collection.
FOR i IN 1 .. 100 LOOP
l_tab.extend;
l_tab(l_tab.last) := i;
END LOOP;
-- cause failure
l_tab(50) := NULL;
l_tab(51) := NULL;
-- perform bulk insert
FORALL i IN l_tab.first .. l_tab.last SAVE EXCEPTIONS
INSERT INTO
exception_test VALUES (l_tab(i));
EXCEPTION
WHEN others THEN
l_error_count := SQL%BULK_EXCEPTIONS.count;
DBMS_OUTPUT.put_line('Number of failures: ' || l_error_count);
FOR i IN 1 .. l_error_count LOOP
DBMS_OUTPUT.put_line('Error: ' || i ||
' Array Index: ' || SQL%BULK_EXCEPTIONS(i).error_index ||
' Message: ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
END LOOP;
END;

PL/SQL RECORDS :-

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

ORACLE supports 3 types of PL/SQL records

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

A table record is declared as follows

E EMP%ROWTYPE;

SELECT * INTO E FROM EMP WHERE EMPNO=7844;

Cursor Recorrds :-

A cursor is declared as follows

CURSOR C1 IS SELECT ENAME,SAL FROM EMP;

-35-
R C1%ROWTYPE;

User Defined Records :-

To use user defined record

 Declare user defined record type


 Declare variable of user defined record type

Declaring User Defined Record type :-

Syntax:-

TYPE <name> IS RECORD


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

R rectype;

Example:-

DECLARE
TYPE rectype IS RECORD
(ENO NUMBER(4),
ENAME VARCHAR2(20),
SAL NUMBER(7,2),
DNAME VARCHAR2(20),
LOC VARCHAR2(20),
GRADE NUMBER(2)
);
TYPE arrtype IS TABLE OF rectype INDEX BY BINARY_INTEGER;
R rectype;
BEGIN
SELECT E.EMPNO,E.ENAME,E.SAL,

-36-
D.DNAME,D.LOC,
S.GRADE BULK COLLECT
INTO R
FROM EMP E, DEPT D, SALGRADE S
WHERE E.DEPTNO = D.DEPTNO AND E.SAL BETWEEN S.LOSAL AND S.HISAL;
FOR I IN R.FIRST..R.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(R(I).EMPNO||’ ‘||R(I).ENAME||’ ‘||R(I).DNAME||’ ‘||
R(I).LOC||’ ‘||R(I).GRADE);
END LOOP;
END;

Named PL/SQL Blocks


 Procedures
 Functions
 Triggers

Difference between ANONYMOUS & NAMED blocks :-

-37-
Anonymous Blocks Named Blocks

Blocks without name Blocks with name

Saved in .SQL file saved in DB

Not Secured Secured

Not Reusable Reusable

Not precompiled Precompiled

Cannot be called can be called from From front-end applns


from front-end

Subprograms :-

PL/SQL allows writing subprograms. A subprogram is named PL/SQL block , made up


of logically grouped SQL and PL/SQL statements that perform a specific task.

A Subprogram:

 Allows writing PL/SQL programs to meet the requirements

 Allows breaking the PL/SQL program into manageable modules

 Allows reusing of code spec and thus improves code maintainability

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


function

 Improves database performance as no compilation is required to execute


the procedure or function

Subprograms can be classified into:

 Procedures

 Functions

Procedures and functions can be invoked from any application that connect to oracle
server. Before a procedure or function is stored, the Oracle engine parses and compiles
the procedure or function.

These subprograms are complied and stored in the Oracle database as stored
programs. Since, these programs are stored in complied form, whenever they are
invoked, only program execution takes place. This saves time would otherwise be
needed for program compilation.

A procedure or function when executed is processed on the Oracle database server,


thus reducing network traffic.

-38-
Procedures:-

Syntax:-

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

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

<Variable Declarations>;

<Constant Declarations>;

BEGIN

<Body code block>;

EXCEPTION

<Exception code block>;

END;

Arguments:-

To make a procedure dynamic it can be passed arguments/parameters before


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

Parameters are 3 types

 IN
 OUT
 IN OUT
IN:-

 IN parameter always receives value from main program.

 The value of an IN parameter is a constant.

 It cannot be changed or reassigned within the module.

 IN parameter can be declared with default value.

 It is default.

OUT:-

 OUT parameter always sends value to main program.

 An OUT parameter is a variable and not a constant

 It can be changed or reassigned within the module

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

-39-
IN OUT:

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

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

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

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

Example:-

Create a procedure to increment particular employee salary by particular


amount.

SQL>ed proc1 ;

CREATE OR REPLACE PROCEDURE


raise_salary(e IN number,amt IN number )
IS
BEGIN
UPDATE emp SET sal = sal + amt WHERE empno = e ;
END;

To create procedure :-

SQL>@proc1 ;

Procedure created (compiled + stored in db)

To execute procedure :-

After creating procedure , procedure can be executed from

Sqlprompt

Another pl/sql block

Any front-end appln

Executing from another pl/sql block :-

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

DECLARE
veno emp.empno%type;
vamt number(4);
BEGIN

-40-
veno := &eno;
vamt := &amount;
raise_salary(veno,vamt); /* call to the procedure */
END;

Executing procedure from sqlprompt :-

SQL>EXECUTE raise_salary(7844,1000);

Using OUT parameter :-

An OUT parameter lets you return values to the caller of a subprogram. Inside the
subprogram, an OUT parameter acts like a variable. That means you can use an OUT
formal parameter as if it were a local variable. You can change its value or reference
the value in any way, as the following example shows:

CREATE OR REPLACE PROCEDURE


raise_salary(e IN number,amt IN number,s OUT number )
IS
BEGIN
UPDATE emp SET sal = sal + amt WHERE empno = e
RETURNING sal INTO s ;
END;

The actual parameter that corresponds to an OUT formal parameter must be a


variable, it cannot be a constant or an expression. For example, the following
procedure call is illegal:

raise_salary(7499, 1000,5000) ; --causes error

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


variable.

Calling block:-

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

DECLARE
veno emp.empno%type;

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

Positional Versus Named Notation for Parameters:-

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

PROCEDURE credit_acct (acct_id INTEGER, amount REAL) IS


BEGIN
UPDATE accts SET bal = bal + amount WHERE acct_no = acct_id;
END;

Calling block :-

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

Using positional notation :-

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

-42-
associates the second actual parameter, amt, with the second formal parameter,
amount.

Using Named Notation

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

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

Using mixed notation :-

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

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

Declaring parameters with DEFAULT values :-

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

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

<Argument2> IN Varchar2 DEFAULT ‘ORACLE CORPORATION’;

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

PROCEDURE create_dept (
new_dname VARCHAR2 DEFAULT 'TEMP',
new_loc VARCHAR2 DEFAULT 'TEMP') IS
BEGIN
INSERT INTO dept
VALUES (deptno_seq.NEXTVAL, new_dname, new_loc);
END;

If an actual parameter is not passed, the default value of its corresponding formal
parameter is used. Consider the following calls to create_dept:

create_dept;
create_dept('MARKETING');
create_dept('MARKETING', 'NEW YORK');

-43-
The first call passes no actual parameters, so both default values are used. The second
call passes one actual parameter, so the default value for new_loc is used. The third
call passes two actual parameters, so neither default value is used.

Usually, you can use positional notation to override the default values of formal
parameters. However, you cannot skip a formal parameter by leaving out its actual
parameter. For example, the following call incorrectly associates the actual parameter
'NEW YORK' with the formal parameter new_dname:

create_dept('NEW YORK'); -- incorrect

You cannot solve the problem by leaving a placeholder for the actual parameter. For
example, the following call is not allowed:

create_dept( , 'NEW YORK'); -- not allowed

In such cases, you must use named notation, as follows:

create_dept(new_loc => 'NEW YORK');

PRAGMA AUTONOMOUS_TRANSACTION :-

If procedure declared with PRAGMA AUTONOMOUS_TRANSACTION then


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

Advantages:-

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


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

-44-
create or replace procedure
raise_salary(e IN number , amt IN number)
IS
pragma autonomous_transaction;
begin
update emp set sal=sal+amt where empno=e;
commit;
end;

calling block :-

begin

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

raise_salary(7566,1000) ; AT committed

rollback; MT ends with rollback;

end;

result:-

7844 update is cancelled


7566 update is committed

Procedure returns single record :-

Create a procedure that accepts empno and returns employee record ?

CREATE OR REPLACE PROCEDURE


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

-45-
Calling block :-

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

Procedure returns multiple records :-

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

CREATE OR REPLACE PROCEDURE


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

Calling block :-

DECLARE
Vdno dept.deptno%type;
c SYS_REFCURSOR;
r emp%rowtype;
BEGIN
vdno := &deptno;
getEmployeeList(vdno,c);
LOOP
FETCH c into r;
EXIT WHEN c%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(r.ename|| ‘ ‘||r.sal);
END LOOP;
CLOSE c;
END;

-46-
Passing Large Data Structures with the 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.

create or replace package employee


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

create or replace procedure


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

-47-
Functions:-

A function is a subprogram that computes a value. Functions and procedures


structured alike except that functions return values using RETURN statement.

Syntax :-

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


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

Example:-

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

SQL>ED fun1 ;

CREATE OR REPLACE FUNCTION


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

To create function :-

SQL>@FUN1

Function created (compiled + stored in db)

After creating function the function can be executed from

Select statement

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

SQL>SELECT getBalance(1000) FROM DUAL;

Scenario:-

CUST_S

CUST_ID CUST_NAME
1 SACHIN RAMESH TENDULKAR
2 MAHINDRA SINGH DHONI
CUST_T

CUST_ID FIRST_NAME MID_NAME LAST_NAME


1 SACHIN RAMESH TENDULKAR
2 MAHINDRA SINGH DHONI

Create required functions and procedures to copy data from CUST_S to CUST_T

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

CREATE OR REPLACE FUNCTION first_name(n IN VARCHAR2)


IS
vfname varchar2(20);
BEGIN
vfname := SUBSTR(n , 1, INSTR(n, ‘ ‘)-1);
RETURN vfname;
END;

2 Create a function to accept name of the person and should return last_name:-

CREATE OR REPLACE FUNCTION last_name(n IN VARCHAR2)


IS
vlname varchar2(20);
BEGIN
vlname := SUBSTR(n , INSTR(n,’ ‘,-1,1)+1);
RETURN vlname;
END;

-49-
3 Create a function to accept name of the person and should return mid_name :-

CREATE OR REPLACE FUNCTION mid_name(n IN VARCHAR2)


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

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

CREATE OR REPLACE PROCEDURE copy_custs_custt


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

PRODUCTS ORDERS

PRODID PNAME PRICE ORDID PRODID QTY


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

-50-
CREATE OR REPLACE FUNCTION total_bill(d IN NUMBER) RETURN NUMBER
IS
CURSOR c1 IS SELECT P.PRODID,P.PRICE,O.QTY
FROM ORDERS O,PRODUCTS P
WHERE O.PRODID = P.PRODID AND O.ORDID=d ;
vvalue NUMBER;
vtbill NUMBER := 0;
BEGIN
FOR r IN c1
LOOP
vvalue := r.qty * r.price
vtbill := vtbill + vvalue;
END LOOP;
RETURN vtbill;
END;

Table Functions:-

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

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

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


performance:

Data processing with unparallelized and unpipelined table functions

-51-
Data processing using pipelining and parallel execution

SQL>create type array as table of number ;


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

Executing Table Function :-


SQL>select * from TABLE(gen_numbers(3));

COLUMN_VALUE
------------
1
2
3
Destroying Procedure or Functions:-

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

Syntax:-

DROP PROCEDURE <ProcedureName>;

Example:-

SQL>DROP PROCEDURE raise_salary;

Syntax:-

DROP FUNCTION <FunctionName>;

-52-
Example:-

SQL>DROP FUNCTION getBalance;

Getting Information About Procedures Or Functions:-

USER_OBJECTS

USER_PROCEDURES

USER_SOURCE

Difference between PROCEDURES and FUNCTIONS:-

PROCEDURE FUNCTION

Need not to return a value must return a value

May return more than one value can return only one value

Returns values using OUT parameter returns value using RETURN expr

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

used to perform DML operations. used to perform computations.

-53-
Packages :-
In the application development world, it is a standard practice to group logically related
blocks such as procedures,functions into modules. Each such module is then accessed
through an interface by application programmers to utilize the implemented
functionality. Oracle allows grouping of procedures , functions,variables,cursors and
exceptions into package.

A package is divided into two parts

 Package Specification
 Package Body

Package Sepcification:-

PACKAGE SPECIFICATION acts as an interface which is visible to programmers. It lists


the available procedures,functions,types .It cannot hold the code.

Syntax :-

CREATE OR REPLACE PACKAGE <name>


As
<declaration of procedures,function,variables >
END;

Package Body:-

 A PACKAGE BODY holds the actual implementation(code).

 A member declared in PACKAGE SPECIFICATION and also defined in BODY is


public.

 A member defined in BODY but not declared in PACKAGE SPECIFICATION is


private.

 Private members can be accessed with in the PACKAGE only.

Syntax:-

CREATE OR REPLACE PACKAGE BODY <NAME>


AS
BEGIN
IMPLEMENTATION OF PROCEDURE, FUNCTIONS .
END;

-54-
Advantages of PL/SQL Packages:-

Packages offer several advantages: modularity, easier application design, information


hiding, added functionality, and better performance.

Modularity:-

Packages let you encapsulate logically related types, items, and subprograms in a
named PL/SQL module.

Information Hiding:-

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

Overloading :-

Package supports overloading , in package you can define two or more subprograms
with same name and with different parameters.

Better Performance:-

When you call a packaged subprogram for the first time, the whole package is loaded
into memory. So, later calls to related subprograms in the package require no disk I/O.
Also, packages stop cascading dependencies and thereby avoid unnecessary
recompiling. For example, if you change the implementation of a packaged function,
Oracle need not recompile the calling subprograms because they do not depend on the
package body.

Example :-

Package to hire & fire employee

Package Specification:-

SQL>ed packspec1

CREATE OR REPLACE PACKAGE employee


AS
PROCEDURE HIRE(E NUMBER,N VARCHAR2,S NUMBER);
PROCEDURE FIRE(E NUMBER);
END;

-55-
To create package specification

SQL>@packspec1

Package created

Package Body:-

SQL>ed packbody1

CREATE OR REPLACE PACKAGE BODY employee


AS
PROCEDURE HIRE(E NUMBER,N VARCHAR2,S NUMBER)
IS
BEGIN
INSERT INTO EMP(EMPNO,ENAME,SAL) VALUES(E,N,S);
COMMIT;
END HIRE;
PROCEDURE FIRE(E NUMBER)
IS
BEGIN
DELETE FROM EMP WHERE EMPNO=E;
COMMIT;
END FIRE;
END;
To create package body

SQL>@packbody1

Package body created

Accessing members of the package :-

Members of the package are accessed by using . operator .

Example :-

SQL>EXECUTE employee.HIRE(1,’A’,5000);

SQL>EXECUTE employee.FIRE(1);

Example:-

ACCT_MASTER

ACCNO NAME BAL


1000 A 10000
1001 B 5000

-56-
ACCT_TRANS

TRID TTYPE TDATE TAMT ACCNO


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

Create a package to implement various bank transactions.

Package specification:-

create or replace package bank


as
procedure new_acct(a number,n varchar2,b number);
procedure close_acct(a number);
function getBalance(a number) return number;
procedure credit(a number,amt number);
procedure debit(a number,amt number);
procedure fund_transfer(s number,t number,amt number);
function getTransList(a number,s date, e date) return sys_refcursor;
function check_acct_exist(a number) return Boolean;
end;
package body :-

create or replace package body bank


as
procedure new_acct(a number, n varchar2, b number)
is
begin
insert into acct_master values(a,n,b);
end new_acct;
procedure close_acct(a number)
is
begin
delete from acct_master where accno=a;
end close_acct;
function getBalance(a number) return number
is
vbal acct_master.bal%type;
begin

-57-
select bal into vbal from acct_master where accno=a;
return vbal;
end getBalance;
function getTrid return number /* private function */
is
vtrid acct_trans.trid%type;
begin
select nvl(max(trid),0)+1 into vtrid from acct_trans;
return vtrid;
end getTrid;
procedure credit(a number,amt number)
is
vtrid acct_trans.trid%type;
begin
update acct_master set bal=bal+amt where accno=a;
vtrid := getTrid;
insert into acct_trans values(vtrid,’D’,sysdate,amt,a);
end credit;
procedure debit(a number,amt number)
is
vtrid acct_trans.trid%type;
begin
update acct_master set bal=bal-amt where accno=a;
vtrid := getTrid;
insert into acct_trans values(vtrid,’W’,sysdate,amt,a);
end debit;
procedure fund_transfer(s number, t number, amt number)
is
begin
debit(s,amt);
credit(t,amt);
end fund_transfer;
function getTransList(a number , s date ,e date) return sys_refcursor
is
c1 sys_refcursor
begin
open c1 for select * from acct_trans
where accno=a and tdate between s and e;
return c1;

-58-
end getTransList;
function check_acct_exist(a number) return Boolean
is
vname acct_master.name%type;
begin
select name into vname from acct_master where accno=a;
return true;
exception
when no_data_found then
return false;
end check_acct_exist;
end;
calling block:-
write a pl/sql block to transfer amount from one account to another account ?

declare
vsaccno acct_master.accno%type;
vtaccno acct_master.accno%type;
vamt acct_trans.tamt%type;
vbal acct_master.bal%type;
begin
vsaccno := &saccno;
vtaccno := &taccno;
vamt := &amount;
if vsaccno = vtaccno then
raise_application_error(-20001,’accts should not be same’);
end if;
if bank.check_acct_exist(vsaccno)=false then
raise_application_error(-20001,’source acct does not exists’);
end if;
if bank.check_acct_exist(vtaccno)=false then
raise_application_error(-20001,’target acct does not exists’);
end if;
vbal := bank.getBalance(vsaccno);
if vamt > vbal then
raise_application_error(-20001,’insufficient balance’);
end if;
bank.fund_transfer(vsaccno,vtaccno,vamt);
exception
when others then

-59-
dbms_output.put_line(sqlerrm);
end;
overloading example :-
create or replace package employee
as
procedure getDetails(e number,n out varchar2);
procedure getDetails(e number, n out varchar2,s out number);
procedure getDetails(e number, n out varchar2,s out number,d out number);
end;
/
create or replace package employee
as
procedure getDetails(e number, n out varchar2)
is
begin
select ename into n from emp where empno=e;
end getDetails;
procedure getDetails(e number, n out varchar2,s out number)
is
begin
select ename,sal into n,s from emp where empno=e;
end getDetails;
procedure getDetails(e number, n out varchar2,s out number,d out number)
is
begin
select ename,sal,deptno into n,s,d from emp where empno=e;
end getDetails;
end;
Droping package specification :-

SQL>DROP PACKAGE <packagename> ;

SQL>DROP PACKAGE BANK ;

The above command drops package specification and body.

SQL>DROP PACKAGE BODY BANK ;

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

Triggers:-

-60-
A trigger is one of the extensively used database objects. It is a piece of code that is
usually run implicitly. A trigger can be defined on:

 Tables/views: DML triggers

 Schema: DDL triggers and logon/logoff event triggers

 Database: Systems event triggers [Startup/Shutdown]

A trigger is commonly used to:

 Generate numbers before inserting new rows

 Prevent invalid transactions

 Permit DML statements against a table only if they are issued, during
regular business hours or on predetermined weekdays.

 Keep an audit trail of a table [i.e. to store the modified and deleted
records of the table] along with the operation performed and the time on
which the operation was performed.

 Ensure that an appropriate column is populated with default information


to generate running numbers before inserting new rows.

 Ensure a rollback, if the information being added/modified in the database


table is found to be inconsistent.

 Populate some other columns based on the new or old column values.

Creating Trigger:-

Syntax:-

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


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

PARTS OF TRIGGERS:-

-61-
A trigger has three basic parts:

Triggering event:-

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


event or user event that cause a trigger to fire.

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

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

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

 A database startup or instance shutdown

 A specific error message or any error message

 A user logon or logoff

Trigger Restriction:-

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

Trigger Action:-

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

TYPES OF TRIGGERS:-

The following are the different types of triggers:-

Row Triggers:-

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

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

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

Statement Triggers:-

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

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

Timed Trigger:-

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

Before Triggers:-

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

After Triggers:-

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

Correlation Names:-

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

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

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

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

Example 1:-

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

Create or replace trigger trg1


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

Example 2:-

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

Employee No :- 1

Old Salary :- 50000

New Salary :- 75000

Difference :- 25000

Solution:

CREATE OR REPLACE TRIGGER DispalyChangesInSalary


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

Example 3 :-

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

CREATE OR REPLACE TRIGGER TRG3


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

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

1 ROW CREATED

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

1 ROW CREATED

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

1 ROW CREATED

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

1 ROW CREATED

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

ERROR :- ORA-20001 :- MAX 4 EMPS

Example 4 :-

JOBS

JOB LOSAL HISAL


CLERK 1000 2000
SALESMAN 1500 3000
MANAGER 2000 4000
Create a trigger that should raise error if new employee salary is out of range
(according to the salary range specified in jobs table).

CREATE OR REPLACE TRIGGER TRG4


BEFORE INSERT ON EMP
FOR EACH ROW
DECLARE
VLSAL JOBS.LSAL%TYPE;
VHSAL JOBS.LSAL%TYPE;
BEGIN
SELECT LSAL,HSAL INTO VLSAL,VHSAL FROM JOBS WHERE JOB=:NEW.JOB;
IF :NEW.SAL NOT BETWEEN VLSAL AND VHSAL THEN
RAISE_APPLICATION_ERROR(-20001,’SAL OUT OF RANGE’);
END IF;
END;

-65-
Testing Trigger :-

SQL>INSERT INTO EMP(EMPNO,ENAME,JOB,SAL,DEPTNO)


VALUES(1,’A’,’CLERK’,3000,10);
ERROR :- ORA-20001 :- SAL OUT OF RANGE

INSTEAD OF Triggers:-

An INSTEAD OF trigger [normally defined on complex views] provides a transparent


way of modifying a view that cannot be modified directly through DML statements
such as INSERT, UPDATE and DELETE.

An INSTEAD OF trigger defined on a view is fired when an INSERT, UPDATE or


DELETE statement is executed against the view.

ORACLE executes INSTEAD OF TRIGGER instead of executing DML operation.

When fired, the INSTEAD OF trigger modifies the underlying tables appropriately.

An INSTEAD OF trigger defined on a view is always a row trigger i.e. the trigger is
fired for each modified row of the view.

INSTEAD OF triggers can be designed only for views and not for tables. The BEFORE
and AFTER options, cannot be used with INSTEAD OF triggers.

Example 5:-

A view named EmpDept is made up of the following columns:

EmpNo
Ename
Sal
DeptNo
DName

CREATE VIEW EmpDept AS


SELECT E.EmpNo, E.Ename, E.Sal, D.DeptNo,D.DName
FROM Emp E , Dept D
WHERE E.DeptNo = D.DeptNo;

Since the view is a complex view i.e. it holds columns from two tables using a JOIN,
data manipulation is not possible.

Example 5:-

-66-
Create a trigger that will help to achieve this.

CREATE OR REPLACE TRIGGER UpdateComplexView


INSTEAD OF INSERT ON EmpDept
FOR EACH ROW
BEGIN
INSERT INTO Emp(EmpNo,Ename,Salary)
VALUES(:NEW.EmpNo,:NEW.Ename,:NEW.Salary);
INSERT INTO Dept(DeptNo, Dname)
VALUES(:NEW.DeptNo, :NEW.DName);
END;

Test trigger :-

SQL>INSERT INTO EMPDEPT VALUES(1,’A’,5000,90,’PURCHASE’);

1 ROW CREATED.

Example 6:-

Similarly to allow deletes via view vwEmpdept, use the following trigger:

CREATE OR REPLACE TRIGGER DeleteComplexView


INSTEAD OF DELETE ON EmpDept
FOR EACH ROW
DECLARE
varEmployees Number;
BEGIN
DELETE FROM Emp WHERE EmpNo =: OLD.EmpNo;
SELECT COUNT(*) INTO varEmployees
FROM Emp WHERE Emp.DeptNo=:OLD.DeptNo;
/*checking if no employee exists in a particular department*/
IF varEmployees = 0 THEN
DELETE FROM Dept WHERE DeptNo =:OLD.Deptno;
END IF;
END;

Mutating Table Error:-

 The Mutating table error is a well-known problem encountered in development


environment.

ORA-04091: table <tablename> is mutating, Trigger /function may not see it

-67-
 The basic reason for this error is the way Oracle manages a read consistent view of
data. The error is encountered when a row-level trigger accesses the same table on
which it is based, while executing. The table is said to be mutating.

 Mutation will not occur if a single record is inserted in the table [using VALUES
caluse]. If bulk insertion is done or data is inserted from another table mutation will
occur.

The mutation is not only encountered during queries, but also for insert, updates and
deletes present in the trigger.

The following table explains various transactions scenarios that involve a trigger and
whether it is prone to generate the mutating error.

Operation Type Mutating

Insert Before/statement-level No

Insert After/statement-level No

Update Before/statement-level No

Update After/statement-level No

Delete Before/statement-level No

Delete After/statement-level No

Insert Before/row-level Single-row Multi-row

No Yes

Insert After/row-level Yes

Update Before/row-level Yes

Update After/row-level Yes

Delete Before/row-level Yes

Delete After/row-level Yes

Example:-

Write a trigger that fires every time the salary of an employee in the Employees table
is updated either due to an insert, update or delete. This trigger should print the total
salary of the available employees after the modification.

-68-
CREATE OR REPLACE TRIGGER DisplayTotalSalary
AFTER INSERT OR UPDATE OR DELETE ON Emp
FOR EACH ROW
DECLARE
varTotalSal Number;
BEGIN
SELECT SUM (Salary) INTO varTotalSal FROM Employees;
DBMS_OUTPUT.PUT(‘Total Salary:’ || varTotalSal);
END;

Test the trigger by issuing the following command:-

UPDATE Employees SET Salary =Salary*1.10 WHERE DeptNo=4;

ORA-4091 :- mutating table error

To avoid mutating table errors:

A row-level trigger must not query or modify a mutating table. However, correlation
names such as NEW and OLD cannot be accessed by the trigger.

A statement-level trigger must not query or modify a mutating table if the trigger is
fired as result of a CASCADE delete.

Auditing :-

Create a trigger to audit DML operations on EMP table into EMP_AUDIT table.

EMP_AUDIT

UNAME OPERATION OPERATION_TIME


SCOTT INSERT ---
SCOTT UPDATE ---
SCOTT DELETE --

CREATE OR REPLACE TRIGGER TRG6


AFTER INSERT OR UPDATE OR DELETE
ON EMP
BEGIN
IF INSERTING THEN
INSERT INTO EMP_AUDIT VALUES(USER,’INSERT’,SYSTIMESTAMP);
ELSIF UPDATING THEN
INSERT INTO EMP_AUDIT VALUES(USER,’UPDATE’,SYSTIMESTAMP);
ELSE

-69-
INSERT INTO EMP_AUDIT VALUES(USER,’DELETE’,SYSTIMESTAMP);
END IF;
END;

DDL Triggers:-

A DDL trigger is a database trigger whose triggering event is a Data Defining Language
[DDL] statement i.e. a DDL trigger fires when a DDL statement [such as a CREATE,
ALTER or DROP statement] is executed in the database or a particular schema.

Example 7:-

Create a DDL trigger to not allow any DDL operation on Sunday

CREATE OR REPLACE TRIGGER TRG7


BEFORE CREATE OR ALTER OR DROP
ON SCHEMA
BEGIN
IF TO_CHAR(SYSDATE,’DY’)=’SUN’ THEN
RAISE_APPLICATION_ERROR(-20001,’NOT TO DO ANY DDL ON SUNDAY’);
END IF;
END;
Example 8:-

The DBA desires to keep track of the user and the date on which objects are created or
dropped in the database.

Write a DDL trigger that inserts s record in the DDLAudit table. This table holds:

EventName
ObjectOwner
ObjectName
ObjectType
Operator
OperationDate

CREATE OR REPLACE TRIGGER DDLAudit


BEFORE CREATE OR DROP ON SCHEMA
BEGIN
INSERT INTO DDLAuditTrail(ora_Sysevent,Ora_Dict_Obj_Owner,
Ora_Dict_obj_Name, Ora_Dict_Obj_Type, USER, SYSDATE );
END ;

-70-
Example 9:-

The DBA desires to daily track the user and the date when the database was shutdown.

Write a database trigger that inserts a record into the DBAAuditTrail

DBAAuditTrail

USER TIME

CREATE OR REPLACE TRIGGER DBAuditTrail

BEFORE SHUTDOWN ON DATABASE

BEGIN

INSERT INTO DBAuditTrail(USER,SYSTIMESTAMP);

END;

Connect to DBA account and SHUTDOWN the database

CONN SYSTEM/MANAGER

SQL>SHUTDOWN

Compound Trigger Example:-

Compound triggers [introduced in Oracle Database 11g] allow defining more than one
type of trigger in a single trigger.

Syntax:-

CREATE OR REPLACE TRIGGER <trigger-name>


FOR <trigger-action> ON <table-name>
COMPOUND TRIGGER
-- Global declaration.
g_global_variable VARCHAR2(10);

BEFORE STATEMENT IS
BEGIN
NULL; -- Do something here.
END BEFORE STATEMENT;

-71-
BEFORE EACH ROW IS
BEGIN
NULL; -- Do something here.
END BEFORE EACH ROW;

AFTER EACH ROW IS


BEGIN
NULL; -- Do something here.
END AFTER EACH ROW;

AFTER STATEMENT IS
BEGIN
NULL; -- Do something here.
END AFTER STATEMENT;

END <TRIGGER-NAME>;

Follows Clause:-

Oracle allows more than one trigger to be created for the same timing point, but it has
never guaranteed the execution order of those triggers. The Oracle 11g trigger syntax
now includes the FOLLOWS clause to guarantee execution order for triggers defined
with the same timing point. The following example demonstrates how follows clause
can be used.

CREATE OR REPLACE TRIGGER trg1


BEFORE INSERT ON EMP
FOR EACH ROW
BEGIN
DBMS_OUTPUT.put_line('TRIGGER 1 - Executed');
END;
Now create second trigger :-

CREATE OR REPLACE TRIGGER trg2


BEFORE INSERT ON EMP
FOR EACH ROW
FOLLOWS trg1 /* trg2 execution follows trg1 */
BEGIN
DBMS_OUTPUT.put_line('TRIGGER 2 - Executed');
END;

-72-
Destroying An Existing Trigger:-

Syntax:-

DROP TRIGGER <TriggerName>;

Example:-

SQL>DROP TRIGGER TRG1;

Disabling Trigger :-

Triggers can be disabled and enabled by using ALTER command.

SQL>ALTER TRIGGER TRG2 DISABLE;

SQL>ALTER TRIGGER TRG2 ENABLE;

We can also disable all the triggers created on a table

SQL>ALTER TABLE EMP DISABLE ALL TRIGGERS;

Querying Triggers Information :-

USER_TRIGGERS

ALL_TRIGGERS

DBA_TRIGGERS

Built-in Packages in PL/SQL:-

-73-
UTL_FILE package :-

With the UTL_FILE package, PL/SQL programs can read data from and write into
operating system text files.

Creating Directory Object :-

CONN SYSTEM / MANAGER

SQL>CREATE DIRECTORY dir1 AS ‘C:\WINDOWS’ ;

SQL>GRANT READ,WRITE ON DIRECTORY dir1 TO SCOTT;

Members of UTL_FILE package :-

FILE_TYPE :- is a Datatype , any variable declared with FILE_TYPE is a file variable.

F1 UTL_FILE.FILE_TYPE ;

FOPEN :- is a function used to open a file for read or write.

UTL_FILE.FOPEN(location ,filename, open_mode,max_linesize) ;

Modes can ‘r’ for read


‘w’ for write
‘a’ for append
F1 := UTL_FILE.FOPEN(‘DIR1’,’abc.txt’,’r’)

PUT_LINE :- is a procedure used to write a line to a file.

UTL_FILE.PUT_LINE(file , data ,autoflush);

UTL_FILE.PUT_LINE(F1,’HELLO WELCOME’);

GET_LINE :- is a procedure used to get a line from file.

UTL_FILE.GET_LINE(file , string variable,len);

UTL_FILE.GET_LINE(F1,s); /* reads a line from file and copies to s */

FCOPY :- is a procedure used to copy contents of one file to newly created file.

UTL_FILE.FCOPY(source_dir ,source_file, dest_dir ,dest_file , start_line , end_line);

FCLOSE :- procedure used to close a file.

UTL_FILE.FCLOSE(file);

UTL_FILE.FCLOSE(F1);

FCLOSE_ALL :- closes all files.

UTL_FILE.FCLOSE_ALL ;

Program to write employee data into a text file :-

-74-
DECLARE
TYPE ETYPE IS TABLE OF EMP%ROWTYPE;
rec ETYPE;
F1 UTL_FILE.FILE_TYPE;
BEGIN
F1 := UTL_FILE.FOPEN(‘DIR1’,’emp.txt’,’w’);
SELECT * BULK COLLECT INTO rec FROM EMP;
FOR I IN rec.FIRST..rec.LAST
LOOP
UTL_FILE.PUT_LINE(F1,rec(I).empno||’ ,’||rec(I).ename||’,’||rec(I).sal);
END LOOP;
UTL_FILE.FCLOSE(F1);
END;

Program to read data from text file :-

DECLARE
F1 UTL_FILE.FILE_TYPE;
rec varchar2(1000);
BEGIN
F1 := UTL_FILE.FOPEN(‘DIR1’,’emp.txt’,’r’);
LOOP
UTL_FILE.GET_LINE(F1,rec);
DBMS_OUTPUT.PUT_LINE(rec);
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
UTL_FILE.FCLOSE(F1);
END;

Dynamic SQL :-

-75-
EXECUTE IMMEDIATE is useful for creating SQL statement that cannot be represented
directly in PL/SQL. It can also be used to build statements where you may not know
the table names , WHERE clauses etc.

Syntax :-

EXECUTE IMMEDIATE ‘dynamic sql statement’ ;

Example :-

DBMS_SQL package :-

The DBMS_SQL package provides an interface to use dynamic SQL to parse any data
manipulation language (DML) or data definition language (DDL) statement using
PL/SQL. For example, you can enter a DROP TABLE statement from within a stored
procedure by using the PARSE procedure supplied with the DBMS_SQL package.

Subprograms of DBMS_SQL package :-

OPEN_CURSOR:-
To process a SQL statement, you must have an open cursor. When you call the
OPEN_CURSOR Function , you receive a cursor ID number for the data structure
representing a valid cursor maintained by Oracle.
Syntax:-
DBMS_SQL.OPEN_CURSOR RETURN INTEGER;
PARSE :-
Every SQL statement must be parsed by calling the PARSE Procedure. Parsing the
statement checks the statement's syntax and associates it with the cursor in your
program. You can parse any DML or DDL statement. DDL statements are run on the
parse, which performs the implicit commit.
Syntax:-
DBMS_SQL.PARSE ( c IN INTEGER,
statement IN VARCHAR2,
language_flag IN INTEGER);
BIND_VARIABLE or BIND_ARRAY:-
Many DML statements require that data in your program be input to Oracle. When you
define a SQL statement that contains input data to be supplied at runtime, you must
use placeholders in the SQL statement to mark where data must be supplied.
EXECUTE:-
Call the EXECUTE function to run your SQL statement.
Syntax:-
DBMS_SQL.EXECUTE ( c IN INTEGER) RETURN INTEGER;
CLOSE_CURSOR:-

-76-
When you no longer need a cursor for a session, close the cursor by calling
CLOSE_CURSOR.
Syntax:-
DBMS_SQL.CLOSE_CURSOR ( c IN OUT INTEGER);

The below procedure deletes all of the employees from the EMP table whose salaries
are greater than the salary that you specify when you run procedure

Example 1:-
CREATE OR REPLACE PROCEDURE delete_emp(salary IN NUMBER) AS
cursor_name INTEGER;
rows_processed INTEGER;
BEGIN
cursor_name := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cursor_name, 'DELETE FROM emp WHERE sal > :x',
DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE(cursor_name, ':x', salary);
rows_processed := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_SQL.CLOSE_CURSOR(cursor_name);
END;
After creating above procedure , it can be invoked as follows

SQL>EXECUTE delete_emp(2000);

After executing above procedure , it deletes all employee records earning more than
2000.

Example 2

The following sample procedure is passed a SQL statement, which it then parses and
runs:

CREATE OR REPLACE PROCEDURE exec_DDL(STRING IN varchar2) AS


cursor_name INTEGER;
ret INTEGER;
BEGIN
cursor_name := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(cursor_name, string, DBMS_SQL.NATIVE);

-77-
ret := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
END;
For example, after creating this procedure, you could make the following call:

SQL>execute exec_DDL('create table acct(c1 integer)');

Example 3 :-

Count no of records in all the tables in current schema ?

DECLARE
s varchar2(1000);
c1 integer;
status integer;
rowcount integer;
totalrows number(10);
BEGIN
for r in (select table_name from user_tables
order by table_name)
LOOP
s := 'select count(*) from '||r.table_name;
c1 := dbms_sql.open_cursor;
dbms_sql.parse(c1,s,dbms_sql.native);
dbms_sql.define_column(c1,1,totalrows);
status := dbms_sql.execute(c1);
rowcount := dbms_sql.fetch_rows(c1);
dbms_sql.column_value(c1,1,totalrows);
dbms_output.put_line(r.table_name||' '||totalrows||' records');
dbms_sql.close_cursor(c1);
END LOOP;
END;

DML Error Logging

Oracle 10g introduced the DML error logging feature.

This feature is useful in situations such as:

-78-
Updating 30 million records. The update operation fails after 30 minutes just because
one of the record amongst those 30 million fails a check constraint.

An INSERT AS SELECT command fails on the row number 899 to 1000 just because
one column value is too large

The DML error logging feature allows adding a clause to the INSERT statement that
causes the 999 correct records to be inserted successfully and the one erroneous
record to be written out to a error logging table for resolving later.

Example:-

Create a table with a few constraints that can be violated for demonstration purpose.

CREATE TABLE StudentAttendance(

StudentNo Varchar2(2) PRIMARY KEY,

Attendance Varchar2(1);

Example:-

Write a PL/SQL program that inserts 100 records in the student Attendance table.

DECLARE
I Number;
BEGIN
I := 1;
WHILE i<=100
LOOP
INSERT INTO StudentAttendance (StudentNo, Attendance) VALUES (I, ‘p’);
DBMS_OUTPUT.PUT_LINE(‘Student No:’ || I);
I := I+1;
END LOOP;
END;

Because 100th time INSERT stmt generates error , so INSERTING 1 to 99 records into
table is cancelled.

Creating The Error Logging Table:-

Create an error logging table to hold the DML errors.

Oracle provides a built-in PL/SQL package named DBMS_ERRLOG, specifically for this
purpose.

BEGIN
DBMS_ERRLOG.CREATE_ERROR_LOG(‘StudentAttendance’, ‘ErrLogAttendance’);
END;

-79-
Logging An Error:-

Once the DML error logging table has been created for a particular table, DML errors
can be logged by adding an error logging clause to the DML statement.

Syntax:-

LOG ERRORS [INTO <Schema>.<Table>] [(<SimpleExpression>)

[REJECT LIMIT {Integer |UNLIMITED}]

Where,

Schema.Table is the name of the error logging table

REJECT LIMIT clause is technically optional, the default reject limit is zero. The
error logging clause is inefffective if a reject limit is not specified.

Simple Expression sub clause allows specifying a statement tag, which is


logged in the ORA_ERR_TAGS fields of the error logging table, to identify which
statement caused the error.

Example:-

DECLARE
I Number;
BEGIN
I := 1;
WHILE I<=100
LOOP
INSERT IINTO StudentAttendance(StudentNo,Attendance) VALUES (I, ‘p’);
LOG ERRORS INTO ErrLogStudentAttendance REJECT LIMIT 1;
DBMS_OUTPUT.PUT_LINE(‘StudentNo: ‘|| I);
I := I + 1;
END LOOP;
END;
Now since the errors are logged, the 99 correct records are populated in the
StudentAttendence table.

-80-

You might also like