PLSQL
PLSQL
PLSQL
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
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 :-
-1-
Error handling :-
-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:-
Anonymous
Named
Anonymous Block:-
-3-
Comments:-
Comments can be used in PL/SQL code blocks in either of the following form:
PL/SQL Variables :-
Temporary storage
Reusability
Ease of maintance
Where,
Scalar datatypes
Composite datatypes
Reference datatypes
-4-
Scalar datatypes :-
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
-5-
Using the assignment operator :=
Syntax:-
<VariableName> := <Value>;
X := 10;
Composite datatypes :-
PL/SQL table
PL/SQL record
Reference Types :-
%TYPE :-
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.
Syntax:-
Control Statements:-
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:-
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:
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:
-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:-
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:-
-10-
Example 1 :-
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.
The entry point into such a block of code is marked using the lables:
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:-
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.
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:-
VALUE_ERROR:-
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;
PL/SQL provides following built-in functions which are used in error handling.
SQLCODE
SQLERRM
SQLCODE returns ERROR CODE
-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 :-
1 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:-
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.
-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:-
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.
Open a file
Process records
Close the file
-17-
Similarly, an explicit cursor can be processed as:
Declaring Cursor:-
Example:-
DECLARE
BEGIN
END;
Opening Cursor:-
Syntax:-
OPEN <CursorName>;
Example:-
OPEN C1;
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:-
Example :-
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:-
%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 :-
Example :-
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;
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.
Syntax:-
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:-
Updates the Username and Password column of the Employees table with the
generated values.
-21-
Sharanam Shah 1 AMSH1
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;
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.
Declaring Cursor:-
Syntax:-
Opening Cursor:-
Syntax:-
FOR <variableName>
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;
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:
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;
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 :-
-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.
1 WEAK
2 STRONG
A REF CURSOR declared without RETURN type is called WEAK REF CURSOR.
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.
-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:
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 :-
Example:-
The company desires to transfer a few employees across the available departments.
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.
1 table
2 varray
PL/SQL TABLE :-
Example:-
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.
TABLE_NAME TYPE_NAME ;
Example:-
X arrtype ;
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 :-
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 .
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;
-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;
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
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.
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 :-
Table records
Cursor records
User defined records
Table Records :-
E EMP%ROWTYPE;
Cursor Recorrds :-
-35-
R C1%ROWTYPE;
Syntax:-
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;
-37-
Anonymous Blocks Named Blocks
Subprograms :-
A Subprogram:
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.
-38-
Procedures:-
Syntax:-
<Variable Declarations>;
<Constant Declarations>;
BEGIN
EXCEPTION
END;
Arguments:-
IN
OUT
IN OUT
IN:-
It is default.
OUT:-
-39-
IN OUT:
Example:-
SQL>ed proc1 ;
To create procedure :-
SQL>@proc1 ;
To execute procedure :-
Sqlprompt
DECLARE
veno emp.empno%type;
vamt number(4);
BEGIN
-40-
veno := &eno;
vamt := &amount;
raise_salary(veno,vamt); /* call to the procedure */
END;
SQL>EXECUTE raise_salary(7844,1000);
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:
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;
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.
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;
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.
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.
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:
To assign a default value to a parameter the assignment operator [:=] or the DEFAULT
keyword can be used.
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:
You cannot solve the problem by leaving a placeholder for the actual parameter. For
example, the following call is not allowed:
PRAGMA AUTONOMOUS_TRANSACTION :-
Advantages:-
-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
raise_salary(7566,1000) ; AT committed
end;
result:-
-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;
Create a procedure that accepts dept no and should return employee list working for
that dept
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:-
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.
-47-
Functions:-
Syntax :-
Example:-
Create a function that accepts account number and should return balance ?
SQL>ED fun1 ;
To create function :-
SQL>@FUN1
Select statement
-48-
Sqlprompt
Another pl/sql block
Executing from SELECT stmt:-
Scenario:-
CUST_S
CUST_ID CUST_NAME
1 SACHIN RAMESH TENDULKAR
2 MAHINDRA SINGH DHONI
CUST_T
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 :-
2 Create a function to accept name of the person and should return last_name:-
-49-
3 Create a function to accept name of the person and should return mid_name :-
PRODUCTS ORDERS
-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.
-51-
Data processing using pipelining and parallel execution
COLUMN_VALUE
------------
1
2
3
Destroying Procedure or Functions:-
Syntax:-
Example:-
Syntax:-
-52-
Example:-
USER_OBJECTS
USER_PROCEDURES
USER_SOURCE
PROCEDURE FUNCTION
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.
-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.
Package Specification
Package Body
Package Sepcification:-
Syntax :-
Package Body:-
Syntax:-
-54-
Advantages of PL/SQL Packages:-
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 Specification:-
SQL>ed packspec1
-55-
To create package specification
SQL>@packspec1
Package created
Package Body:-
SQL>ed packbody1
SQL>@packbody1
Example :-
SQL>EXECUTE employee.HIRE(1,’A’,5000);
SQL>EXECUTE employee.FIRE(1);
Example:-
ACCT_MASTER
-56-
ACCT_TRANS
Package specification:-
-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 :-
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:
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.
Populate some other columns based on the new or old column values.
Creating Trigger:-
Syntax:-
PARTS OF TRIGGERS:-
-61-
A trigger has three basic parts:
Triggering event:-
The trigger fires automatically when any of the following events occur:
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:-
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
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
Difference :- 25000
Solution:
Example 3 :-
-64-
SQL>INSERT INTO EMP(EMPNO,DEPTNO) VALUES(1,10);
1 ROW CREATED
1 ROW CREATED
1 ROW CREATED
1 ROW CREATED
Example 4 :-
JOBS
-65-
Testing Trigger :-
INSTEAD OF Triggers:-
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:-
EmpNo
Ename
Sal
DeptNo
DName
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.
Test trigger :-
1 ROW CREATED.
Example 6:-
Similarly to allow deletes via view vwEmpdept, use the following trigger:
-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.
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
No 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;
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
-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:-
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
-70-
Example 9:-
The DBA desires to daily track the user and the date when the database was shutdown.
DBAAuditTrail
USER TIME
BEGIN
END;
CONN SYSTEM/MANAGER
SQL>SHUTDOWN
Compound triggers [introduced in Oracle Database 11g] allow defining more than one
type of trigger in a single trigger.
Syntax:-
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 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.
-72-
Destroying An Existing Trigger:-
Syntax:-
Example:-
Disabling Trigger :-
USER_TRIGGERS
ALL_TRIGGERS
DBA_TRIGGERS
-73-
UTL_FILE package :-
With the UTL_FILE package, PL/SQL programs can read data from and write into
operating system text files.
F1 UTL_FILE.FILE_TYPE ;
UTL_FILE.PUT_LINE(F1,’HELLO WELCOME’);
FCOPY :- is a procedure used to copy contents of one file to newly created file.
UTL_FILE.FCLOSE(file);
UTL_FILE.FCLOSE(F1);
UTL_FILE.FCLOSE_ALL ;
-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;
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 :-
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.
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:
-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:
Example 3 :-
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;
-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.
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.
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:-
Where,
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.
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-