Index: Introduction To PL/SQL
Index: Introduction To PL/SQL
Chapter page No
1 introduction to pl/sql 02
2 control statements 16
3 Loops 25
4 cursors 32
6 Exception Handling 80
7 PL/SQL subprograms 97
10 packages 128
11 triggers 144
Introduction to PL/SQL
1
PL/SQL is oracle corporation’s procedural extensions to SQL . PL/SQL is a database-
oriented programming language that extends Oracle SQL with procedural capabilities
by adding programming constructs found in procedural languages, resulting in a
structural language that is more powerful than SQL.
PL/SQL was developed by Oracle in early 90’s to enhance the capabilities of SQL.
A procedural language allows writing a program that specifies a list of operations to be
performed sequentially to achieve the desired result.
Benefits of PL/SQL :-
Improved performance :-
PL/SQL has several advantages . For example using SQL*PLUS we can send SQL
statement to the ORACLE server one at a time. Each SQL statement results in another
call to the ORACLE server hence multiple SELECT statements will result in multiple
round trips adding significantly to the network traffic.
When these SQL statements are combined into a PL/SQL block they are sent to the
ORACLE server as a single unit. The SQL statements in PL/SQL block are executed at
the server. The server sends the result of these SQL statements back to the client also
as a single unit. So number of round trips are reduced and performance improves.
Portability:-
You can move PL/SQL programs to any host environment (operating system or
platform) that supports ORACLE and PL/SQL. In other words PL/SQL programs can run
anywhere the ORACLE server can run.
Control structures :-
2
Error handling :-
PL/SQL ENVIRONMENT :-
Blocks of PL/SQL are passed to and processed by a PL/SQL engine , which may reside
within the tool or within the oracle server. The engine that is used depends on where
the pl/sql block is being invoked from.
Pl/sql engine separates the sql statements and sends them individually to the SQL
statement executor.A single transfer is required to send the block from the application
to the oracle server.thus improves performance , especially in a client /server network.
Anonymous
Named
3
Anonymous Block:- Anonymous PL/SQL Block consists of three sections:
Declaration Section:-The PL/SQL Block Declare section should start with keyword
called ‘DECLARE‘.Declaration section is not mandatory in a block. Declaration section is
used to declare any placeholders (that stores data temporarily) like variables,
constants, records and cursors which are used in the execution section.
Execution Section:-
The PL/SQL Block Execution section should start with keyword ‘BEGIN’ and should end
with keyword ‘END’. This section is a mandatory and it is used to write the
programming logic to achieve business requirement. Constructs like loops, conditional
statement and SQL statements are part of execution section.
Exception Section:
The PL/SQL Block Exception section should start with keyword ‘EXCEPTION’.
Exception section is not mandatory. This section is used to handle any error/exception
raised in the program. Exception handling section is the place where exception
handlers are defined to handle the run time errors.
DBMS_OUTPUT.PUT_LINE :-
This statement is used to display information on the screen. It is very helpful when you
want to see how your PL/SQL block is executed and you might want to see how
variables change their values throughout the program in order to debug it.
Ex :- DBMS_OUTPUT.PUT_LINE(‘hello’);
4
Syntax:-
SQL>SET SERVEROUTPUT ON
BEGIN
DBMS_OUTPUT.PUT_LINE('Welcome to – Naresh IT');
END;
Comments:-
Comments can be used in PL/SQL code blocks in either of the following form:
PL/SQL Variables :-
Temporary storage
Reusability
Ease of maintance
Where,
PL/SQL Data Types:-Every variable ,constant and parameter has a data type that
determines its storage format and valid range of values, and operations that can be
performed on it. PL/SQL provides many predefined data types.
5
Composite Data items that have internal components that
can be accessed individually.
Large Object (LOB) Pointers to large objects such as images or text etc
Scalar datatypes :-
6
Number(n,m) Blob
Char(n) Clob
Varchar2(n) Nclob
Date Bfile
Timestamp PLS_integer
Interval Day to Second Binary_integer
Interval Year to Month Binary_float
Long Binary_Double
Long raw Boolean
Raw
Declaring Variables:-
In any programming language variables play a vital role in building program logic. We
cannot imagine a code without using variables. Variables are like placeholders we use
them to store/manipulate data at the time of PL/SQL block execution.
Syntax :-
variablename datatype(size);
Example :-
x NUMBER;
Declaring a constant
Syntax:-
7
<VariableName> := <Value>;
X := 10;
Composite datatypes :-
PL/SQL table
PL/SQL record
Reference Types :-
%TYPE :-
veno emp.empno%type;
In the above example datatype declared for empno in emp table is assigned to
Variable veno.
%ROWTYPE :-
The %ROWTYPE attribute provides a record type that represents a row in a database
table. The record can store an entire row of data selected from the table or fetched
from a cursor or cursor variable.
Example:-
r emp%rowtype;
in the above example variable r can store an entire row of data fetched from table
emp.
SQL>ed prg1
DECLARE
a NUMBER(2);
b NUMBER(2);
c NUMBER(3);
BEGIN
a := 10;
b := 20;
c := a+b;
dbms_output.put_line(c);
END;
/
8
SQL>@prg1
Output :- 30
When the above program is rerun the program displays the same output 30 because
variables a,b assigned with static values . to input values for a , b after running the
Program use substitution variables prefixed with “&”.
Program using Substitution Variables :-
SQL>ed prg2
DECLARE
a NUMBER(2);
b NUMBER(2);
c NUMBER(3);
BEGIN
a := &a;
b := &b;
c := a+b;
dbms_output.put_line(c);
END;
/
SQL>@prg2
Enter value for a :- 40
Enter value for b := 70
Output :- 110
Program to find biggest and smallest number :-
DECLARE
a number(2);
b number(2);
BEGIN
a := &a;
b := &b;
DBMS_OUTPUT.PUT_LINE('big='||greatest(a,b));
DBMS_OUTPUT.PUT_LINE('small='||least(a,b));
END;
/
After executing the program
Enter value for a :-50
Enter value for b :-90
Output :- big=90
Small=50
Interacting With Oracle Server From PL/SQL :-
9
to Interact with oracle server from PL/SQL , SQL statements needs to embedded in
PL/SQL program.the following statements can be directly embedded in PL/SQL program
DML statements (INSERT,UPDATE,DELETE)
DRL statement (SELECT)
TCL statements (COMMIT,ROLLBACK)
Retrieving Data From Database Using PL/SQL :-
In PL/SQL in SELECT statement INTO clause is mandatory and occurs between SELECT
and FROM clause. It is used to specify the names of the variables to hold values
returned by SELECT statement.we must use one variable for one item selected.
Program to Input Empno and Print Name and Salary of the Employee :-
DECLARE
vempno Number(4);
vename Varchar2(20);
vsal Number(7,2);
BEGIN
/* Using assignment operator. */
vEmpno := &Empno;
/* Using direct assignment from table column to variables */
SELECT ename,sal INTO vename,vsal
FROM EMP
WHERE EMPNO=vEmpno;
/* Printing a name and salary. */
DBMS_OUTPUT.PUT_LINE(‘Name :- ‘||vename);
DBMS_OUTPUT.PUT_LINE(‘Sal :- ‘||vsal);
END;
SQL>@prg3
Enter Empno :- 7369
output :- Name :- SMITH
Sal :- 800
Program to Input Empno and Print Experience of the Employee :-
DECLARE
v_eno emp.empno%type;
v_doj emp.hiredate%type;
v_expr number(2);
BEGIN
10
v_eno := &eno;
SELECT hiredate into v_doj
FROM emp
WHERE empno = v_eno;
v_expr := round((sysdate-v_doj)/365);
DBMS_OUTPUT.PUT_LINE('Expr= '||v_expr||' years');
END;
/
Programt to Input Empno and Calculate Total Salary Paid to Employee Till Last
Month ?
DECLARE
v_eno emp.empno%type;
v_sal emp.sal%type;
v_comm emp.comm%type;
v_doj emp.hiredate%type;
v_totsal number(11,2);
BEGIN
v_eno := &eno;
SELECT sal,comm,hiredate into v_sal,v_comm,v_doj
FROM emp
WHERE empno=v_eno;
v_totsal := round(months_between(ADD_MONTHS(LAST_DAY(sysdate),-
1),v_doj)*(v_sal+nvl(v_comm,0)));
DBMS_OUTPUT.PUT_LINE('TOTAL SALARY='||v_totsal);
END;
Frequently Asked Questions :-
What is PL/SQL?
PL/SQL is a procedural language that has both interactive SQL and procedural
programming language constructs such as iteration, conditional branching.
PL/SQL uses block structure as its basic structure. Anonymous blocks or nested
blocks can be used in PL/SQL.
11
Some scalar data types such as NUMBER, VARCHAR2, DATE, CHAR, LONG, BOOLEAN.
Some composite data types such as RECORD & TABLE.
Currently, the maximum parsed/compiled size of a PL/SQL block is 64K and the
maximum code size is 100K.
Anonymous blocks are unnamed blocks which are not stored anywhere while sub
programs are compiled and stored in database. Anonymous blocks compile at run time.
Sql>Declare
v_emp_joining date;
Begin
v_emp_joining := ’1 April 1985′;
End;
The given code creates a compilation error because the v_empJoining variable is
declared as the date datatype but has been assigned a character value. To correct the
code, you should use the conversion function, to_date, as follows:
sql>Declare
v_emp_joining date;
Begin
v_emp_joining := to_date(’1 April1985′, ‘DD month yyyy’);
End;
Can SQL group functions be used within the procedural statements?
The SQL group functions, such as AVG, MIN, MAX, COUNT, SUM, STDDEV, and
VARIANCE, cannot be directly used within PL/SQL. However, they can be embedded
within SQL statements and then can be utilized in the procedural statements.
Sql> Begin
Create table emp_dept(emp_code varchar2(10),dept_code Varchar2(10));
End;
The code will raise a parsing error as the block has a DDL statement, which is not
allowed directly within a PL/SQL block. The DBMS_SQL package should be used to use
a DDL statement.
Any variable declared within a block is local to the block and global to the nested
blocks. The variables declared within the nested blocks are unknown to the outer
blocks. They override the references to any outer declared variables with the same
name unless they are used with the block label name.
12
What are the different datatypes that can be defined in a PL/SQL block?
1.Scalar — Holds a single value and does not contain any internal component. Some of
its data types are given as follows:
Sql> Declare
vx number;
vy number;
1_result number;
13
2_result number;
Begin
vx :=10;
vy :=20;
1_result :=vx*vy;
2_result :=vx+vy;
End;
/
The variable names in PL/SQL cannot begin, with a numeric character. In PL/SQL,
identifier should begin with an alphabet.
•Anonymous blocks, as the name suggests, are PL/SQL blocks that are not given any
name and cannot be stored in the database.
•These blocks can call another blocks but they cannot be called by any other block, as
they do not have a name.
•An anonymous block is executed by either storing the block code in a file or writing
the block code at the SQL prompt.
The BEGIN and END keywords are mandatory for any PL/SQL block.
What are Transaction Control statements? Can they be used within the
PL/SQL block?
14
An Oracle supplied package called DBMS_OUTPUT is used to display data within a
PL/SQL block.
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 <Condition1> THEN
STATEMENTS;
ELSIF <Condition2> THEN
STATEMENTS;
ELSE
STATEMENTS;
END IF;
3 Nested if :-
IF <Condition> THEN
IF <Condition> THEN
STATEMENTS;
ELSE
STATEMENTS;
END IF;
END IF;
Program to Input Empno and Increment Employee Salary as follows
15
vJob emp.job%type;
BEGIN
vEmpno := &empno;
SELECT job INTO vJob
FROM EMP
WHERE EMPNO=vEmpno;
IF vJob=’CLERK’ THEN
UPDATE EMP SET SAL=SAL*1.1 WHERE EMPNO=vEmpno;
ELSIF vJob=’MANAGER’ THEN
UPDATE EMP SET SAL=SAL*1.2 WHERE EMPNO=vEmpno;
ELSE
UPDATE EMP SET SAL=SAL*1.05 WHERE EMPNO=vEmpno;
END IF;
END;
Program to Input Name of the Person and Print First, Mid , Last Names :-
DECLARE
v_name varchar2(60);
v_fname varchar2(20);
v_mname varchar2(20);
v_lname varchar2(20);
BEGIN
v_name := trim('&person_name');
IF instr(v_name,' ') =0 then
v_fname := v_name;
ELSE
v_fname := substr(v_name,1,instr(v_name,' ')-1);
v_lname := substr(v_name,instr(v_name,' ',-1,1)+1);
v_mname := trim(ltrim(rtrim(v_name,v_lname),v_fname));
END IF;
DBMS_OUTPUT.PUT_LINE('First Name :-'||v_fname);
DBMS_OUTPUT.PUT_LINE('Middle Name :-'||v_mname);
DBMS_OUTPUT.PUT_LINE('Last Name :-'||v_lname);
END;
/
16
Pl/sql program to implement Bank Transaction (debit,credit) :-
Create the following two tables and sequence to generate values for TRID
ACCT_MASTER ACCT_TRANS
ACCNO ACNAME BAL TRID TTYPE TDATE TAMT ACCNO
1 Kumar 10000
Sequence :- CREATE SEQUENCE bank_seq
MINVALUE 1
INCREMENT BY 1
MAXVALUE 9999999;
DECLARE
v_accno acct_master.accno%type;
v_ttype acct_trans.ttype%type;
v_amt acct_trans.tamt%type;
v_bal acct_master.bal%type;
v_trid acct_trans.trid%type;
BEGIN
v_accno := &accno;
v_ttype := '&ttype';
v_amt := &amount;
SELECT bal into v_bal FROM acct_master WHERE accno = v_accno;
IF v_ttype='w' and v_amt > v_bal then
DBMS_OUTPUT.PUT_LINE('insufficient funds');
ELSIF v_ttype='w' and v_amt <= v_bal then
update acct_master set bal=bal-v_amt where accno=v_accno;
v_trid := tseq.nextval;
insert into acct_trans(trid,ttype,tamt,accno)
values(v_trid,'w',v_amt,v_accno);
ELSIF v_ttype='d' then
update acct_master set bal=bal+v_amt where accno = v_accno;
v_trid := tseq.nextval;
insert into acct_trans(trid,ttype,tamt,accno)
values(v_trid,'d',v_amt,v_accno);
ELSE
DBMS_OUTPUT.PUT_LINE('invalid transaction');
END IF;
COMMIT;
end;
CASE Statements :-
17
A CASE statement has two forms Simple CASE and Searched CASE. Simple CASE
statement allows you to specify a selector that determines which group of actions to
take. A searched CASE statement does not have a selector it has search conditions
that are evaluated in order to determine which group of actions to take.
Simple CASE statement :-
CASE SELECTOR
WHEN EXPRESSION1 THEN STATEMENT 1;
WHEN EXPRESSION2 THEN STATEMENT 2;
--------------------------------
ELSE
STATEMENT N;
END CASE;
If SELECTOR = EXPRESSION1 then STATEMENT 1 is executed , IF SELECTOR =
EXPRESSION2 then STATEMENT 2 is executed Otherwise ELSE statement is executed.
Example 1 :-
DECLARE
v_date DATE ;
v_day VARCHAR2(1);
BEGIN
v_date := SYSDATE;
v_day := TO_CHAR(v_date,’D’);
CASE v_day
WHEN ‘1’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Sunday’);
WHEN ‘2’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Monday’);
WHEN ‘3’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Tuesday’);
WHEN ‘4’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Wednesday’);
WHEN ‘5’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Thursday’);
WHEN ‘6’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Friday’);
ELSE
DBMS_OUTPUT.PUT_LINE(‘Today is Saturday’);
END CASE;
END;
Example 2:-
Pl/sql program to input empno and increment employee salary as follows
18
if employee deptno=10 increment salary by 10%
if employee deptno=20 increment salary by 15%
if employee deptno=30 incremnet salary by 20%
Others increment salary by 5%
DECLARE
vempno EMP.EMPNO%TYPE;
vdeptno EMP.DEPTNO%TYPE;
BEGIN
vempno := &empno;
SELECT deptno INTO vdeptno
FROM emp
WHERE empno=vempno;
CASE vdeptno
WHEN 10 THEN
UPDATE emp SET sal=sal*1.1 WHERE empno=vempno;
WHEN 20 THEN
UPDATE emp SET sal=sal*1.15 WHERE empno=vempno;
WHEN 30 THEN
UPDATE emp SET sal=sal*1.2 WHERE empno=vempno;
ELSE
UPDATE emp SET sal=sal*1.05 WHERE empno=vempno;
END CASE;
END;
Searched CASE :-
CASE
WHEN COND1 THEN STATEMENT 1;
WHEN COND2 THEN STATEMENT 2
----------------------
ELSE
STATEMENT N;
END CASE ;
If COND1 evaluates to TRUE then STATEMENT 1 is executed if COND2
evaluates to TRUE then STATEMENT 2 is executed otherwise ELSE statement is
executed.
Difference between Simple CASE and Searched CASE :-
In simple CASE statements execution is based on Selector in searched CASE
statements execution is based on Condition. Use simple CASE if condition based on “=”
operator use searched CASE if condition based on other than “=” operator .
Example 1:-
BEGIN
19
CASE
WHEN TO_CHAR(sysdate,’D’)= ‘1’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Sunday’);
WHEN TO_CHAR(sysdate,’D’)= ‘2’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Monday’);
WHEN TO_CHAR(sysdate,’D’)= ‘3’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Tuesday’);
WHEN TO_CHAR(sysdate,’D’)= ‘4’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Wednesday’);
WHEN TO_CHAR(sysdate,’D’)= ‘5’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Thursday’);
WHEN TO_CHAR(sysdate,’D’)= ‘6’ THEN
DBMS_OUTPUT.PUT_LINE(‘Today is Friday’);
ELSE
DBMS_OUTPUT.PUT_LINE(‘Today is Saturday’);
END CASE;
END;
Example 2:-
STUDENT
SID SNAME S1 S2 S3 STOT SAVG SRES
100 vinod 87 66 89
101 rahul 95 75 30
Write a PL/SQL program to input sid and calculate & update STOT, SAVG, SRES ?
DECLARE
vsid STUDENT.SID%TYPE
vs1 STUDENT.S1%TYPE;
vs2 STUDENT.S2%TYPE;
vs3 STUDENT.S3%TYPE;
vtot STUDENT.STOT%TYPE;
vavg STUDENT.SAVG%TYPE;
vres STUDENT.SRES%TYPE;
BEGIN
vsid := &sid;
SELECT S1,S2,S2 INTO vs1,vs2,vs3
FROM STUDENT
WHERE sid = vsid;
vtot := vs1+vs2+vs3;
vavg := ROUND((vs1+vs2+vs3)/3,2);
CASE
WHEN vs1>=35 AND vs2>=35 AND vs3>=35 THEN
20
vres := ‘PASS’ ;
ELSE
vres := ‘FAIL’ ;
END CASE ;
UPDATE STUDENT SET stot=vtot ,
savg=vavg,
sres=vres
WHERE sid=vsid;
END;
Nested CASE :- a CASE statement can be Nested in another CASE statement as
follows.
CASE
WHEN condition 1 THEN
CASE Selector
WHEN Expression1 THEN
Statement 1;
WHEN Expression2 THEN
Statement 2;
----
ELSE
Statement N;
END CASE;
WHEN condition 2 THEN
---------------
END CASE;
Example 1:-
increment employee salary as follows
if deptno=10 if job=’CLERK’ then increment sal by 10%
if job=’MANAGER’ then increment sal by 20%
for others increment sal by 5%
if deptno=20 if job=’CLERK’ then increment sal by 15%
if job=’MANAGER’ then increment sal by 25%
for others increment sal by 10%
for other depts. Increment sal by 12%
DECLARE
veno EMP.EMPNO%TYPE;
vjob EMP.JOB%TYPE;
vdno EMP.DEPTNO%TYPE;
vincr NUMBER;
BEGIN
21
veno := &eno;
SELECT DEPTNO,JOB INTO vdno,vjob
FROM emp
WHERE empno=veno;
CASE vdno
WHEN 10 THEN
CASE vjob
WHEN 'CLERK' THEN
vincr := 1.1;
WHEN 'MANAGER' THEN
vincr := 1.2;
ELSE
vincr := 1.05;
END CASE;
WHEN 20 THEN
CASE vjob
WHEN 'CLERK' THEN
vincr := 1.15;
WHEN 'MANAGER' THEN
vincr := 1.25;
ELSE
vincr := 1.1;
END CASE;
ELSE
vincr := 1.12;
END CASE;
UPDATE emp SET sal=sal*vincr WHERE empno=veno;
commit;
END;
/
Iterative Statements:-
Simple Loop
While Loop
For Loop
Simple Loop:-
A Simple Loop repeats a set of statements in the PL/SQL code block at least once
before the loop terminates. When the EXIT condition is satisfied the process exists
from the loop.
Syntax:-
22
LOOP
<Sequence of statements>
EXIT WHEN <condition>;
END LOOP;
Example:- pl/sql program to print numbers from 1 to 50 ?
DECLARE
X NUMBER(2) := 1;
BEGIN
LOOP
DBMS_OUTPUT.PUT_LINE(X);
X := X+1;
EXIT WHEN X>50;
END LOOP;
END;
/
WHILE Loop:-
A While Loop repeats a set of statements in the PL/SQL code block as long as a
condition is true. The condition is evaluated when iteration begins. The iteration
continues until the condition becomes false.
Syntax:-
WHILE <Condition>
LOOP
<STATEMENTS> ;
END LOOP;
A While Loop can be constructed as follows:
Example 1:-
Write a pl/sql program to input two dates and print number of Sundays between those
two dates.
DECLARE
D1 DATE;
D2 DATE;
CNT NUMBER := 0;
BEGIN
D1 := ‘&DATE1’;
D2 := ‘&DATE2’;
23
D1 := NEXT_DAY(D1-1,’SUNDAY’);
WHILE(D1<=D2)
LOOP
CNT := CNT+1;
D1 := D1+7;
END LOOP;
DBMS_OUTPUT.PUT_LINE(‘ No of Sundays :-‘||CNT);
END;
Input :- DATE1 :- 01-JAN-14
DATE2 :- 31-DEC-14
Output :- 52
FOR Loop:-
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:-
Example 1 :-
BEGIN
FOR x IN 1..50
LOOP
DBMS_OUTPUT.PUT_LINE(i);
END LOOP;
END;
The above program prints numbers from 1 to 50
24
Example 2 :-
BEGIN
FOR x IN REVERSE 1..50
LOOP
DBMS_OUTPUT.PUT_LINE(i);
END LOOP;
END;
The above program prints numbers from 5O TO 1.
Example 3:-
Write a pl/sql program to input a string and print reverse of that string ?
DECLARE
S1 VARCHAR2(20);
S2 VARCHAR2(20);
L NUMBER;
BEGIN
S1 := ‘&STRING’;
L := LENGTH(S1);
FOR I IN 1..L
LOOP
S2 := S2 || SUBSTR(S1,-I,1);
END LOOP;
DBMS_OUTPUT.PUT_LINE(‘Reverse of that string :- ‘|| S2);
END;
input :- Enter String :- WELCOME
Output :- EMOCLEW
GOTO Statement:-
Often times, it is required to shift the control of the program from a set of statements
to some other set of statements. This means the statements in the code block will not
execute in sequence as it usually does.
The GOTO statement helps achieving this, by allowing shifting the control from one
block of code to some other block of code within a PL/SQL block.
The entry point into such a block of code is marked using the lables:
25
The GOTO statement can then make use of this user-defined name to jump into that
block of code for execution. The label must be in scope when the GOTO statement is
encountered.
Syntax:-
DECLARE
v_eno emp.empno%type;
e emp%rowtype;
BEGIN
v_eno := &eno;
select * into e from emp where empno=v_eno;
dbms_output.put_line(e.empno||' '||e.ename||' '||e.sal);
END;
/
DML and Records :- from Oracle 9i you can use records in INSERT and UPDATE
statements. In INSERT , UPDATE commands you can use records that are based on
%ROWTYPE variable against the table to which INSERT is made.
DECLARE
my_emp emp%ROWTYPE;
BEGIN
my_emp.empno := 1004;
my_emp.ename := ‘ sanjay’;
my_emp.job := ‘CLERK’;
my_emp.mgr := NULL;
my_emp.hiredate := sysdate;
my_emp.sal := 4000;
my_emp.comm := 200;
my_emp.deptno := 10;
INSERT INTO emp VALUES my_emp;
COMMIT;
END;
Notice that you do not include parentheses around the record specifier.If you this
format
INSERT INTO emp VALUES(my_emp) ; INVALID
26
Then you will get an ORA-00947 : not enough values exception.
Record-based updates :- From oracle 9i you also perform updates of an entire row
with a record. The following example Inserts a row into the books table with a
%ROWTYPE record.
Example :-
DECLARE
my_dept dept%ROWTYPE;
BEGIN
my_dept.deptno := 10;
my_dept.dname := ‘ACCOUNTING’
my_dept.loc := ‘LONDON’;
UPDATE dept
SET ROW=my_dept
WHERE deptno = my_dept.deptno;
COMMIT;
END;
Notice that i used a new keyword , ROW, to indicate that I am updating entire
row with a record.
The EXIT statement can be used to end a FOR-LOOP unconditionally, as shown in the
following code snippet:
No, the DECODE function cannot be used within the procedural statements. It can only
be used within the SQL statements, as shown in the following code:
SELECT DECODE(emp_rating, NULL, 1500. ‘C’, 4000 ‘B’, 6000, ‘A’, 8000)
In the preceding code, if the emp_rating column is null, the DECODE function returns
the value 1500.
27
The EXIT statement is used to force a loop to terminate unconditionally. When an
EXIT statement is encountered within a loop, the loop is completed and the control is
passed to the next statement after the loop. Following is the syntax of the EXIT
statement:
LOOP
…..
IF condition THEN
EXIT;
END IF;
END LOOP;
The EXIT-WHEN statement terminates a loop when the given condition is fulfilled.
When the EXIT statement is encountered, first the condition is checked. If the condition
is TRUE, then the loop is completed and the control is passed to the statement after
the loop. Following is the syntax of the EXIT-WHEN statement:
LOOP
…..
IF condition THEN
END IF;
END LOOP;
The WHILE-LOOP associates a condition with a loop. First, the condition is checked
and then the loop is started. The loop continues until the condition remains TRUE. If
the condition becomes FALSE, the loop is completed.
The number of iterations is not known as it is based only on a condition. The loop may
not be executed at all, as the condition is checked at the start of the loop itself.
In a FOR-LOOP, can the counter value be assigned a value within the loop?
The counter of a FOR-LOOP can only be used as a constant within the loop.
It cannot be assigned a value inside the loop, as that would change the bounds of the
loop, which is logically not possible.
Cursors :-
28
In order for Oracle to process an SQL statement, it needs to create an area of
memory known as the context area also called Active Data Set. A cursor is a pointer to
the context area. Through the cursor , a PL/SQL program can control the context area.
The data that the query retrieves from table(s) is held in a cursor opened in the
memory by Oracle. This data is then transferred to the client machine when demanded
it.
When a cursor is declared, a pointer is returned which initially point to nothing. When
the cursor is opened, appropriate memory is allocated and the cursor structure is
created. The cursor variable now points to the cursor area in the memory. When the
cursor is closed the memory allocated for the cursor is released.
Cursors are usually used to hold the data that is retrieved from table(s) and perform
actions on that data one row at a time.
Types Of Cursors:-
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
Similarly, an explicit cursor can be processed as:
29
Now that the cursor is declared, the next logic step is to use it. A cursor can be put to
use, by opening, fetching the data and finally closing it when not required.
Declaring Cursor:-
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.
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.
30
Closing Cursor:-
After the data is fetched from the Active Data Set, the cursor needs to be closed. The
CLOSE command helps achieving this. Closing a cursor releases the memory occupied
by the cursor and it’s Active Data Set.
Syntax:-
CLOSE<CursorName>;
Example :-
CLOSE C1;
After a cursor is closed, it can be opened again using the open command.
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.
31
%ISOPEN :-
C1%FOUND C1%ROWCOUNT
C1%NOTFOUND C1%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;
/
Using %ROWTYPE :-
DECLARE
CURSOR curEmp IS
SELECT ename,sal FROM emp;
r curEmp%ROWTYPE;
BEGIN
OPEN curEmp;
LOOP
FETCH curEmp INTO r;
EXIT WHEN curEmp%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(r.ename||’ ‘||r.sal);
END LOOP;
CLOSE curEmp;
32
END;
/
Note :- %ROWTYPE variable reduces no of simple variables required in a
program.
Example :-
DECLARE
CURSOR C1 IS SELECT DISTINCT SAL FROM EMP ORDER BY SAL DESC;
r C1%ROWTYPE;
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO r;
EXIT WHEN C1%ROWCOUNT>5 OR C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(r.SAL);
END LOOP;
CLOSE C1;
END;
/
In the above program C1%ROWCOUNT>5 will be true if table contains more than 5
rows and C1%NOTFOUND will be true if table contains less than 5 rows.
Example : a pl/sql cursor program display even number of records from emp table
using %rowcount attribute?
DECLARE
CURSOR C1 IS SELECT ENAME, SAL FROM EMP;
V_ENAME VARCHAR2(20);
V_SAL NUMBER(10);
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO V_ENAME, V_SAL;
EXIT WHEN C1%NOTFOUND;
IF MOD(C1%ROWCOUNT,2)=0 THEN
DBMS_OUTPUT.PUT_LINE(V_ENAME||' ' ||V_SAL);
END IF;
END LOOP;
CLOSE C1;
END;
Example :- a PL/SQL program to check whether a record exists in table or not using
cursor.
DECLARE
cursor c1 is SELECT 'X' FROM emp WHERE empno=7844;
33
s varchar2(1);
BEGIN
OPEN C1;
FETCH C1 INTO s;
IF C1%FOUND THEN
DBMS_OUTPUT.PUT_LINE('Employee exists');
ELSE
DBMS_OUTPUT.PUT_LINE('No Such Employee');
END IF;
END;
/
Using While Loop:-
DECLARE
CURSOR C1 IS SELECT ename,sal FROM emp;
Vename emp.ename%type;
Vsal emp.sal%type;
BEGIN
OPEN C1;
FETCH C1 INTO vename,vsal;
WHILE C1%FOUND
LOOP
DBMS_OUTPUT.PUT_LINE(vename||’ ‘||vsal);
FETCH C1 INTO vename,vsal;
END LOOP;
CLOSE C1;
END;
Syntax:-
34
FOR <Variable> IN <CursorName>
LOOP
Statements;
END LOOP;
The above loop is executed number of times number of records are there in cursor.
Every time for loop is executed a record is fetched from cursor and the record is
assigned to ROWTYPE variable (loop index).
Example:-
DECLARE
CURSOR C1 IS SELECT ename,sal FROM emp;
BEGIN
FOR r IN C1
LOOP
DBMS_OUTPUT.PUT_LINE(r.ename||’ ‘||r.sal);
END LOOP;
END;
/
Example :-
Updates the Username and Password column of the Employees table with the
generated values.
DECLARE
CURSOR curEmp IS
SELECT EmployeeNo, FirstName, LastName, Salary FROM Employees;
varUname Employees.Username%TYPE;
varPwd Employees.Password%TYPE;
BEGIN
FOR r IN curEmp
LOOP
35
varUname :=SUBSTR(r.FirstName, -2, 2)
||SUBSTR(r.LastName, 1, 2)|| r.EmployeesNo;
varPwd :=SUBSTR(r.FirstName, 1, 2)
||SUBSTR(r.LastName, 1, 2)
||SUBSTR(TO_CHAR(r.Salary), -2, 2);
UPDATE Employees SET Username = varUname,
Password = varPwd
WHERE EmployeeNo = r.EmployeeNo;
END LOOP;
COMMIT;
END;
Defining An Inline Cursor:-
The FOR loop, allows defining an inline cursor. An inline cursor is a cursor that is not
declared in the DECLARE section but the cursor definition is included in the FOR loop.
Example:-
BEGIN
FOR r in (SELECT ename,sal FROM emp)
LOOP
DBMS_OUTPUT.PUT_LINE(r.ename||’ ‘||r.sal);
END LOOP;
END;
Parameterized Cursor:-
Often times, it is required to have a generic SQL query that retrieves data based on a
parameter it receives. SQL queries allows this using a WHERE clause.
Similarly, the cursor that uses an SQL query to retrieve the required data can be
passed a parameter which in turn can be passed to the WHERE condition of the actual
query.
Declaring Cursor:-
Syntax:-
Opening Cursor:-
Syntax:-
36
OPEN <CursorName> (<Value> | <variable> | <Expression>);
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;
Example:-
DECLARE
CURSOR C1(d number,j varchar2) is
SELECT ENAME,SAL,JOB,DEPTNO FROM EMP
WHERE DEPTNO=d AND job=j;
BEGIN
FOR e in C1(&dno,’&job’);
LOOP
DBMS_OUTPUT.PUT_LINE(e.ENAME||e.SAL||e.JOB||e.DEPTNO);
END LOOP;
END;
/
Cursor Within Cursor Example :-
The company holds Employees and Departments details in Master tables called Emp
and Dept. For reporting purposes, it is required to store the information from these
tables in a denormalized table EmpDept.
Fetches departments from the Departments table For every department, fetche
employees belonging to that department from the Employees table and Add all the
information from both the tables to the EmpDept table
DECLARE
CURSOR c1 IS SELECT * FROM Dept;
CURSOR c2(d Dept.DeptNo%TYPE)
IS SELECT * FROM Emp WHERE DeptNo =d;
BEGIN
FOR d IN c1
37
LOOP
FOR e IN c2(d.deptno)
LOOP
INSERT INTO EmpDept(EmpNo,Ename,Sal,Deptno,Dname,Loc)
VALUES (e.EmpNo,e.Ename,e.Sal,d1.Deptno,d1.Dname,d1.Loc)
END LOOP;
END LOOP;
COMMIT;
END;
Example :- a PL/SQL program that displays list of table names and displays list of
columns belongs to that table.
BEGIN
FOR t IN (SELECT TABLE_NAME FROM USER_TABLES)
LOOP
DBMS_OUTPUT.PUT_LINE(t.TABLE_NAME);
FOR c IN (SELECT COLUMN_NAME FROM USER_TAB_COLUMNS
WHERE TABLE_NAME=t.TABLE_NAME);
LOOP
DBMS_OUTPUT.PUT_LINE(c.COLUMN_NAME);
END LOOP;
END LOOP;
END;
Locking Cursor Data:-
When a SELECT query is fired to retrieve data, no locks are placed on the selected
rows. It is required, to lock a set of records before the changes are applied by a
program using PL/SQL. Oracle provides FOR UPDATE clause that can be used with a
SELECT statement to perform this locking.
If the FOR UPDATE clause is used with SELECT query, Oracle obtains an exclusive row-
level lock on all the rows identified by the SELECT statement. On applying an exclusive
lock, no one else will be able to change any of the records until a ROLL BACK or a
COMMIT is fired.
Syntax :-
38
2 CURSOR C1 IS SELECT * FROM EMP FOR UPDATE OF SAL NOWAIT;
If the rows have been already locked by another session then NOWAIT option
returns error.
Wait n Waits for n seconds and returns error if other session still locking
the rows at the end of the time.
4 if cursor is based on a join of two tables , we may want to lock the rows of
one table but not the other. To do this we specify any column of that table.for
example
DECLARE
CURSOR c1 IS SELECT * FROM EMP
FOR UPDATE NOWAIT;
BEGIN
FOR e IN C1
LOOP
UPDATE EMP SET SAL=SAL*1.1 WHERE EMPNO=e.EMPNO;
END LOOP;
COMMIT;
END;
CURRENT OF clause :-
Used to access current row of a cursor current of clause used in update, delete
statements only. Whenever we are using a where current of clause we must use for
update clause .after processing the records we must release the locks using commit.
consider a table with two columns ENAME,SAL
EMP
ENAME SAL
A 5000
B 6000
A 4000
B 5000
C 3000
A Pl/sql program to increment each employee salary by 1000
DECLARE
39
CURSOR C1 IS SELECT * FROM EMP
FOR UPDATE OF SAL NOWAIT;
BEGIN
FOR r IN C1
LOOP
UPDATE EMP SET SAL=SAL+1000 WHERE ENAME=r.ENAME;
END LOOP;
COMMIT;
END;
After executing the above program A,B salaries are increment by 2000 because of the
duplicate values so to overcome the above problem use CURRENT OF clause in WHERE
Condition as follows
DECLARE
CURSOR C1 IS SELECT * FROM EMP
FOR UPDATE OF SAL NOWAIT;
BEGIN
FOR r IN C1
LOOP
UPDATE EMP SET SAL=SAL+1000 WHERE CURRENT OF C1;
END LOOP;
COMMIT;
END;
/
Example:-Write a pl/sql program to update F3 as follows
T1
F1 F2 F3
5 4 F1+F2
5 4 F1-F2
5 4 F1*F2
5 4 F1/F2
DECLARE
CURSOR C1 IS SELECT * FROM T1 FOR UPDATE OF F3;
BEGIN
FOR I IN C1
LOOP
IF (C1%ROWCOUNT=1) THEN
UPDATE T1 SET F3=F1+F2 WHERE CURRENT OF C1;
ELSIF (C1%ROWCOUNT=2) THEN
40
UPDATE T1 SET F3=F1-F2 WHERE CURRENT OF C1;
ELSIF (C1%ROWCOUNT=3) THEN
UPDATE T1 SET F3=F1*F2 WHERE CURRENT OF C1;
ELSE
UPDATE T1 SET F3=F1/F2 WHERE CURRENT OF C1;
END IF;
COMMIT;
END;
CURSOR Variables (REF cursor):-
A cursor variable differs from cursors the way constants differs from variables. A
cursor is static , a cursor variable is dynamic. Cursors always points to same work
area,while cursor variable can point to different work areas.
You can use a cursor variable to pass result set of a query between stored
procedures.
1 WEAK
2 STRONG
A REF CURSOR declared without RETURN type is called WEAK REF CURSOR.
PL/SQL provides a built-in WEAK REF CURSOR called SYS_REFCURSOR.
Example :-
DECLARE
C1 SYS_REFCURSOR;
E EMP%ROWTYPE;
BEGIN
OPEN C1 FOR SELECT * FROM EMP;
LOOP
FETCH C1 INTO E;
EXIT WHEN C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(E.ENAME||E.SAL);
END LOOP;
END;
Strong REF Cursor:-
A REF CURSOR declared with RETURN type is called STRONG REF CURSOR. In pl/sql
there is a REF CURSOR datatype , where REF stands for reference and CURSOR stands
for the class of the object.
41
To create a cursor variable
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:
42
For implicit cursor, Oracle provides attributes that can be used to access status
information such as:
Last Insert
Last Update
Last Delete
Last Single-Row select statements
Attributes:-
%FOUND :-
Returns TRUE if last SQL statement affects a row otherwise returns FALSE.
%NOTFOUND :-
Returns TRUE if last SQL statement doesn’t affects a row otherwise returns a FALSE.
%ROWCOUNT :-
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;
Let us take another example where exactly implicit cursor use useful. For example
consider a table which stores customer details opens account in bank
43
ACCT_MASTER
A property called atomocity i.e. either all operations must be done or none must be
done. If individual operations are successful then commit the transaction and if any of
DECLARE
V_SACCNO CUSTOMER.ACCNO%TYPE;
V_TACCNO CUSTOMER.ACCNO%TYPE;
V_AMT CUSTOMER.BAL%TYPE;
CNT1 NUMBER;
CNT2 NUMBER;
BEGIN
V_SACCNO := &SACCNO;
V_TACCNO := &TACCNO;
V_AMT := &AMOUNT;
UPDATE CUSTOMER SET BAL=BAL-V_AMT WHERE ACCNO=V_SACCNO;
CNT1 := SQL%ROWCOUNT;
UPDATE CUSTOMER SET BAL=BAL+V_AMT WHERE ACCNO=V_TACCNO;
CNT2 := SQL%ROWCOUNT;
IF (CNT1=1 AND CNT2=1) THEN
COMMIT;
ELSE
ROLLBACK;
END IF;
END;
Frequently asked questions:-
Cursor is a named private SQL area from where information can be accessed.
Cursors are required to process rows individually for queries returning multiple rows.
Explain the two types of Cursors?
There are two types of cursors
Implict Cursor
Explicit Cursor
44
Explain How CURSOR FOR LOOP works ?
A cursor FOR loop opens a cursor, repeatedly fetches rows of values from the result set
into fields in the record, then closes the cursor when all rows have been processed.
What are the PL/SQL Statements used in cursor processing?
DECLARE CURSOR cursor name,
OPEN cursor name,
FETCH cursor name INTO <variable list> or Record types,
CLOSE cursor name.
What are the cursor attributes used in PL/SQL?
%ISOPEN - to check whether cursor is open or not
% ROWCOUNT - number of rows fetched/updated/deleted.
% FOUND - to check whether cursor has fetched any row. True if rows are fetched.
% NOT FOUND - to check whether cursor has fetched any row. True if no rows are
Fetched.
What is WHERE CURRENT OF clause?
PL/SQL provides the WHERE CURRENT OF clause for both UPDATE and DELETE
statements inside a cursor.This allows you to easily make changes to the most recently
fetched row of data.
Can we pass parameter to a cursor what are the advantages ?
PL/SQL also allows you to pass parameters into cursors. It eases your work because:
- A parameter makes the cursor more reusable.
- A parameter avoids scoping problems.
How can you find a cursor is opened or not in PL/SQL block ?
Using %ISOPEN attribute
45
SQL%FOUND
SQL%NOTFOUND
SQL%ROWCOUNT
SQL%ISOPEN
How is it possible to fetch a current row from a cursor without using ROWID?
The WHERE CURRENT OF clause is used to reference the current row of an active
explicit cursor. When this clause is used, ROWID is not needed to access the current
row for modifications.
You can use the SQL%NOTFOUND attribute to check if the UPDATE statement has
successfully updated any rows or not. This attribute returns the TRUE value if the last
executed SQL statement has not affected any rows.
The status of a cursor can be determined by checking the attributes of that cursor.
Following are the attributes of a cursor:
=> %FOUND
=> %NOTFOUND
=> %ISOPEN
=> %ROWCOUNT
What will be the value of %N0TF0UND attribute after a cursor is opened but
before a FETCH statement is executed?
The value of %NOTFOUND attribute will be NULL after the cursor is opened but before
the FETCH statement is executed. After the FETCH statement, the value of
%NOTFOUND attribute will be TRUE OR FALSE depending on the result of the FETCH
statement.
Why does SQL%ISOPEN always returns the FALSE value?
SQL%ISOPEN always evaluates to FALSE as ORACLE closes the implicit cursor as soon
as it executes the query.
A loop must be defined to fetch multiple rows from an explicit cursor. It will fetch one row
from the active cursor set on each iteration of the loop.
Collections and Records :-
In a collection, the internal components are always of the same data type, and are
called elements. You access each element by its unique subscript. Lists and arrays are
classic examples of collections.
46
In a record, the internal components can be of different data types, and are
called fields. You access each field by its name. A record variable can hold a table row,
or some columns from a table row. Each record field corresponds to a table column.
The following are the Different Collections in PL/SQL
1 Associative array (Index by Table)
2 Variable Array
3 Nested Table
Characterstics of PL/SQL collections :-
Dense means that the collection has no gaps between elements—every element between
the first and last element is defined and has a value.
Associative Arrays or Index By Tables :-
An associative array (also called an index-by table) is a set of key-value pairs. Each
key is unique, and is used to locate the corresponding value. The key can be either an
integer or a string. To use Index by Table in PL/SQL block
47
Declaring a Variable of PL/SQL table type:-
TABLE_NAME TYPE_NAME ;
Example:-
X arrtype ;
Example 1 :-
DECLARE
TYPE arrtype IS TABLE OF number(3)
INDEX BY BINARY_INTEGER;
X arrtype;
BEGIN
FOR I IN 1..10
LOOP
X(I) := I*10;
END LOOP;
FOR I IN 1..10
LOOP
DBMS_OUTPUT.PUT_LINE(X(I));
END LOOP;
END;
Understanding Nested Tables:-
An array has a declared number of elements, but a nested table does not. The
size of a nested table can increase dynamically.
An array is always dense (that is, it always has consecutive subscripts). A
nested array is dense initially, but it can become sparse, because you can
delete elements from it.
48
The below figure shows a varray named Grades, which has maximum size 10 and
contains seven elements. The current upper bound for Grades is 7, but you can
increase it to the maximum of 10. Grades(n) references the nth element of Grades.
Varray of Size 10
Nested tables and associative arrays differ in persistence and ease of parameter
passing.
A nested table can be stored in a database column; therefore, you can use a nested
table to simplify SQL operations in which you join a single-column table with a larger
table. An associative array cannot be stored in the database.
PL/SQL automatically converts between host arrays and associative arrays that
use numeric key values. The most efficient way to pass collections to and from
the database server is to set up data values in associative arrays, and then use
those associative arrays with bulk constructs (the FORALLstatement
or BULK COLLECT clause).
When stored in the database, varrays keep their ordering and subscripts.
A varray is stored as a single object. If a varray is less than 4 KB, it is stored inside the
table of which it is a column; otherwise, it is stored outside the table but in the same
tablespace.
You must store or retrieve all elements of a varray at the same time, which is
appropriate when operating on all the elements at once. However, this might be
impractical for large numbers of elements.
49
You must delete or update some elements, but not all elements at once.
You would create a separate lookup table, with multiple entries for each row of
the main table, and access it through join queries.
You cannot rely on the order and subscripts of a nested table remaining stable as the
nested table is stored in and retrieved from the database, because the order and
subscripts are not preserved in the database.
You can define TABLE and VARRAY types in the declarative part of any PL/SQL block,
subprogram, or package using a TYPE definition.
For nested tables and varrays declared within PL/SQL, the element type of the table or
varray can be any PL/SQL data type except REF CURSOR.
When defining a VARRAY type, you must specify its maximum size with a positive
integer. In the following example, you define a type that stores up to 366 dates:
DECLARE
TYPE Calendar IS VARRAY(366) OF DATE;
Associative arrays let you insert elements using arbitrary key values. The keys need
not be consecutive.
The key data type can be PLS_INTEGER, VARCHAR2.
Declaring an Associative Array:
DECLARE
TYPE EmpTabTyp IS TABLE OF employees%ROWTYPE
INDEX BY PLS_INTEGER;
emp_tab EmpTabTyp;
BEGIN
/* Retrieve employee record. */
SELECT * INTO emp_tab(100) FROM employees
WHERE employee_id = 100;
END;
/
Declaring Nested Tables, Varrays, and Associative Arrays :-
DECLARE
TYPE nested_type IS TABLE OF VARCHAR2(30);
TYPE varray_type IS VARRAY(5) OF INTEGER;
TYPE assoc_array_num_type
IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
50
TYPE assoc_array_str_type
IS TABLE OF VARCHAR2(32) INDEX BY PLS_INTEGER;
TYPE assoc_array_str_type2
IS TABLE OF VARCHAR2(32) INDEX BY VARCHAR2(64);
v1 nested_type;
v2 varray_type;
v3 assoc_array_num_type;
v4 assoc_array_str_type;
v5 assoc_array_str_type2;
BEGIN
-- an arbitrary number of strings can be inserted into v1
v1 := nested_type('Shipping','Sales','Finance','Payroll');
v2 := varray_type(1, 2, 3, 4, 5); -- Up to 5 integers
v3(99) := 10; -- Just start assigning to elements
v3(7) := 100; -- Subscripts can be any integer values
v4(42) := 'Smith'; -- Just start assigning to elements
v4(54) := 'Jones'; -- Subscripts can be any integer values
v5('Canada') := 'North America';
-- Just start assigning to elements
v5('Greece') := 'Europe';
-- Subscripts can be string values
END;
Specifying Collection Element Types with %TYPE and %ROWTYPE:-
DECLARE
-- Nested table type that can hold an arbitrary number of employee IDs.
-- The element type is based on a column from the EMPLOYEES table.
-- You need not know whether the ID is a number or a string.
TYPE EmpList IS TABLE OF emp.empno%TYPE;
-- Declare a cursor to select a subset of columns.
CURSOR c1 IS SELECT emp FROM emp;
-- Declare an Array type that can hold information about 10 employees.
-- The element type is a record that contains all the same fields as the
EMPLOYEES table.
TYPE Senior_Salespeople IS VARRAY(10) OF emp%ROWTYPE;
-- Declare a cursor to select a subset of columns.
CURSOR c2 IS SELECT ename,sal FROM emp;
-- Array type that can hold a list of names. The element type is a record that
contains the same fields as the cursor (that is ename,sal).
TYPE NameList IS VARRAY(20) OF c2%ROWTYPE;
51
BEGIN
NULL;
END;
/
Declaring NOT NULL Constraint on Collection Elements:-
DECLARE
TYPE EmpList
IS TABLE OF emp.empno%TYPE NOT NULL;
vemp EmpList := EmpList(100, 150, 160, 200);
BEGIN
v_employees(3) := NULL; -- assigning NULL raises an exception
END;
/
Data Type Compatibility for Collection Assignment:-
DECLARE
TYPE last_name_typ IS VARRAY(3) OF VARCHAR2(64);
TYPE surname_typ IS VARRAY(3) OF VARCHAR2(64);
-- These first two variables have the same data type.
group1 last_name_typ := last_name_typ('Jones','Wong','Marceau');
group2 last_name_typ := last_name_typ('Klein','Patsos','Singh');
-- This third variable has a similar declaration, but is not the same type.
group3 surname_typ := surname_typ('Trevisi','Macleod','Marquez');
BEGIN
-- Allowed because they have the same data type
group1 := group2;
-- Not allowed because they have different data types
-- group3 := group2; -- raises an exception
END;
/
Comparing Two Nested Tables:-
DECLARE
TYPE dnames_tab IS TABLE OF VARCHAR2(30);
dept_names1 dnames_tab :=
dnames_tab('Shipping','Sales','Finance','Payroll');
dept_names2 dnames_tab :=
dnames_tab('Sales','Finance','Shipping','Payroll');
dept_names3 dnames_tab :=
dnames_tab('Sales','Finance','Payroll');
BEGIN
52
-- You can use = or !=, but not < or >.
-- These 2 are equal even though members are in different order.
IF dept_names1 = dept_names2 THEN
DBMS_OUTPUT.PUT_LINE(‘dept_names1 and dept_names2 same’):
END IF;
IF dept_names2 != dept_names3 THEN
DBMS_OUTPUT.PUT_LINE(‘dept_names2 and dept_names3 not same’);
END IF;
END;
Using Collection Methods:-
The only collection method that you can use with an empty collection is EXISTS; all
others raise the exception COLLECTION_IS_NULL.
Collection Methods :-
EXISTS method :-
EXISTS(n) returns TRUE if the nth element in a collection exists; otherwise, it returns
FALSE. By You can also use EXISTS to avoid referencing a nonexistent element, which
raises an exception. When passed an out-of-range subscript, EXISTS returns FALSE
instead of raising SUBSCRIPT_OUTSIDE_LIMIT.
DECLARE
53
TYPE NumList IS TABLE OF INTEGER;
n NumList := NumList(1,3,5,7);
BEGIN
IF n.EXISTS(1) THEN
END IF;
END IF;
END IF;
END;
For nested tables, COUNT usually equals LAST. However, if you delete elements from
the middle of a nested table, COUNT becomes smaller than LAST. When tallying
elements, COUNT ignores deleted elements. Using DELETE with no parameters sets
COUNT to 0.
DECLARE
n NumList := NumList(2,4,6,8);
BEGIN
54
DBMS_OUTPUT.PUT_LINE
DBMS_OUTPUT.PUT_LINE
DBMS_OUTPUT.PUT_LINE
DBMS_OUTPUT.PUT_LINE
END;
LIMIT returns the maximum number of elements that a collection can have. If the
collection has no maximum size, LIMIT returns NULL.
DECLARE
dept_names dnames_var :=
dnames_var('Shipping','Sales','Finance','Payroll');
BEGIN
DBMS_OUTPUT.PUT_LINE
DBMS_OUTPUT.PUT_LINE
DBMS_OUTPUT.PUT_LINE
|| 'dept_names.EXTEND() is '
|| (dept_names.LIMIT - dept_names.COUNT));
END;
55
/
Finding the First or Last Collection Element (FIRST and LAST Methods):-
For a collection indexed by integers, FIRST and LAST return the first and last (smallest
and largest) index numbers. For an associative array indexed by strings, FIRST and
LAST return the lowest and highest key values. If the collection is empty, FIRST and
LAST return NULL. If the collection contains only one element, FIRST and LAST return
the same value.
Example f shows how to use FIRST and LAST to iterate through the elements
in a collection that has consecutive subscripts.:-
DECLARE
n NumList := NumList(1,3,5,7);
counter INTEGER;
BEGIN
LOOP
END LOOP;
-- Start at the first element and look for the next element until there are no more.
counter := n.FIRST;
LOOP
DBMS_OUTPUT.PUT_LINE
56
counter := n.NEXT(counter);
END LOOP;
ELSE
END IF;
END;
For varrays, FIRST always returns 1 and LAST always equals COUNT.For nested tables,
normally FIRST returns 1 and LAST equals COUNT. But if you delete elements from the
beginning of a nested table, FIRST returns a number larger than 1. If you delete
elements from the middle of a nested table, LAST becomes larger than COUNT.When
scanning elements, FIRST and LAST ignore deleted elements.
PRIOR(n) returns the index number that precedes index n in a collection. NEXT(n)
returns the index number that succeeds index n. If n has no predecessor, PRIOR(n)
returns NULL. If n has no successor, NEXT(n) returns NULL.For associative arrays with
VARCHAR2 keys, these methods return the appropriate key value; ordering is based on
the binary values of the characters in the string.
These methods are more reliable than looping through a fixed set of subscript values,
because elements might be inserted or deleted from the collection during the loop. This
is especially true for associative arrays, where the subscripts might not be in
consecutive order and so the sequence of subscripts might be (1,2,4,8,16) or
('A','E','I','O','U').
DECLARE
n NumList := NumList(1966,1971,1984,1989,1999);
BEGIN
n.DELETE(3);
DBMS_OUTPUT.PUT_LINE
57
('Now the element after #2 is #' || n.NEXT(2));
DBMS_OUTPUT.PUT_LINE
END IF;
END;
You can use PRIOR or NEXT to traverse collections indexed by any series of subscripts.
DECLARE
n NumList := NumList(1,3,5,7);
counter INTEGER;
BEGIN
counter := n.FIRST;
LOOP
DBMS_OUTPUT.PUT_LINE
counter := n.NEXT(counter);
END LOOP;
counter := n.LAST;
LOOP
DBMS_OUTPUT.PUT_LINE
58
('Counting down: Element #' || counter || ' = ' || n(counter));
counter := n.PRIOR(counter);
END LOOP;
END;
You cannot use EXTEND with index-by tables. You cannot use EXTEND to add elements
to an uninitialized collection. If you impose the NOT NULL constraint on a TABLE or
VARRAY type, you cannot apply the first two forms of EXTEND to collections of that
type.
EXTEND operates on the internal size of a collection, which includes any deleted
elements. This refers to deleted elements after using DELETE(n), but not DELETE
without parameters which completely removes all elements. If EXTEND encounters
deleted elements, it includes them in its tally. PL/SQL keeps placeholders for deleted
elements, so that you can re-create them by assigning new values.
DECLARE
n NumList := NumList(2,4,6,8);
x NumList := NumList(1,3);
output VARCHAR2(128);
BEGIN
LOOP
output :=
59
output || NVL(TO_CHAR(the_list(i)),'NULL') || ' ';
END LOOP;
DBMS_OUTPUT.PUT_LINE(output);
END;
BEGIN
DBMS_OUTPUT.PUT_LINE
DBMS_OUTPUT.PUT_LINE
print_numlist(n);
DBMS_OUTPUT.PUT_LINE
DBMS_OUTPUT.PUT_LINE
print_numlist(x);
END;
When it includes deleted elements, the internal size of a nested table differs from the
values returned by COUNT and LAST. This refers to deleted elements after using
DELETE(n), but not DELETE without parameters which completely removes all
elements. For example, if you initialize a nested table with five elements, then delete
elements 2 and 5, the internal size is 5, COUNT returns 3, and LAST returns 4. All
deleted elements, regardless of position, are treated alike.
60
Note:
DECLARE
n NumList := NumList(1,2,3,5,7,11);
output VARCHAR2(128);
BEGIN
IF n.COUNT = 0 THEN
ELSE
LOOP
output :=
END LOOP;
DBMS_OUTPUT.PUT_LINE(output);
END IF;
END;
BEGIN
print_numlist(n);
print_numlist(n);
print_numlist(n);
print_numlist(n);
BEGIN
61
n := NumList(1,2,3);
n.TRIM(100);
EXCEPTION
DBMS_OUTPUT.PUT_LINE
END;
n := NumList(1,2,3,4);
n.TRIM(2);
print_numlist(n);
END;
DECLARE
courses CourseList;
BEGIN
62
elements 1 and 2. Instead, it trims valid element 2
courses.TRIM(courses.COUNT);
END;
In general, do not depend on the interaction between TRIM and DELETE. It is better to
treat nested tables like fixed-size arrays and use only DELETE, or to treat them like
stacks and use only TRIM and EXTEND.
DELETE with no parameters removes all elements from a collection, setting COUNT to
0.
DELETE(n) removes the n th element from an associative array with a numeric key or a
nested table. If the associative array has a string key, the element corresponding to
the key value is deleted. If n is null, DELETE(n) does nothing.
DELETE(m,n) removes all elements in the range m..n from an associative array or
nested table. If m is larger than n or if m or n is NULL, DELETE(m,n) does nothing.
DECLARE
n NumList := NumList(10,20,30,40,50,60,70,80,90,100);
nicknames NickList;
BEGIN
nicknames('Bob') := 'Robert';
63
nicknames('Buffy') := 'Esmerelda';
nicknames('Chip') := 'Charles';
nicknames('Dan') := 'Daniel';
nicknames('Fluffy') := 'Ernestina';
nicknames('Rob') := 'Robert';
nicknames.DELETE('Chip');
nicknames.DELETE('Buffy','Fluffy');
END;
Varrays always have consecutive subscripts, so you cannot delete individual elements
except from the end by using the TRIM method. You can use DELETE without
parameters to delete all elements.
If an element to be deleted does not exist, DELETE(n) simply skips it; no exception is
raised. PL/SQL keeps placeholders for deleted elements, so you can replace a deleted
element by assigning it a new value. This refers to deleted elements after using
DELETE(n), but not DELETE without parameters which completely removes all
elements.
Normally a developer will use a cursor to retrieve and process multiple rows of data,
one at a time, but there are performance problems when dealing with large numbers of
rows using cursors. As we have seen, a cursor fetches one row at a time, holding a
consistent view, until all rows have been retrieved or the cursor is closed.
A performance issue arises from the fact that there are two engines in the database,
the PL/SQL engine and the SQL engine. When a cursor fetches a row of data it
performs a “context switch” to the SQL engine, and it is the SQL component that
retrieves the data. The SQL engine places the data in-memory and another context
switch places us back into the PL/SQL engine.
The PL/SQL engine then continues processing until the next row is required, and the
process repeats. A context switch is very fast, but if performed over and over again,
the switching can take a noticeable amount of time. A bulk collect is a method of
fetching data where the PL/SQL engine tells the SQL engine to collect many rows at
once and place them in a collection. The SQL engine retrieves all the rows and loads
64
them into the collection and switches back to the PL/SQL engine. All the rows are
retrieved with only 2 context switches. The larger the number of rows processed, the
more performance is gained by using a bulk collect.
In the Oracle10g database, the PL/SQL engine may perform a bulk collect for you. In
10g, a cursor loop may cause the PL/SQL engine to automatically bulk collect 100 rows
at a time, allowing your code to process rows without having to setup and execute the
bulk collect operation. As a result of this performance enhancement in 10g, bulk
collecting 75 rows may not provide you with much of a benefit, while bulk collecting
large numbers of rows (many hundreds) will still provide you with increased
performance.
Example:-
DECLARE
TYPE etype IS TABLE OF varchar2(20) INDEX BY BINARY_INTEGER;
e etype;
BEGIN
SELECT ENAME BULK COLLECT INTO e FROM EMP;
FOR x IN e.FIRST.e.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(e(x));
END LOOP;
END;
Note :- prior to oracle 9i R2 we can bulk collect only single column values, from 9i R2
we can also bulk collect records .
BulkCollecting Records :-
DECLARE
TYPE etype IS TABLE OF EMP%ROWTYPE INDEX BY BINARY_INTEGER;
E etype;
x number;
BEGIN
SELECT * BULK COLLECT INTO E FROM EMP;
x := E.FIRST;
WHILE(x <= E.LAST)
LOOP
DBMS_OUTPUT.PUT_LINE(E(X).EMPNO||’ ‘||E(X).ENAME||’ ‘||E(X).SAL);
x := E.NEXT(x);
END LOOP;
END;
65
A PL/SQL program to display all employee records (backward navigation):-
DECLARE
TYPE etype IS TABLE OF EMP%ROWTYPE INDEX BY BINARY_INTEGER;
e etype;
x number;
BEGIN
SELECT * BULK COLLECT INTO e FROM EMP;
x := e.LAST;
WHILE(x >= e.FIRST)
LOOP
DBMS_OUTPUT.PUT_LINE(e(x).EMPNO||’ ‘||e(x).ENAME||’ ‘||e(x).SAL);
x := e.PRIOR(x);
END LOOP;
END;
BULK BIND using FORALL loop :-
The FORALL syntax allows us to bind the contents of a collection to a single DML
statement, allowing the DML to be run for each row in the collection without requiring a
context switch each time. FOR ALL loop improves performance of BULK INSERT,BULK
UPDATE,BULK DELETE.
FORALL loop allows only statement and that must be DML statement.
Syntax :-
FORALL <var> in <low>..<upp>
DML statement
Example for BULK INSERT:-
EMP_TEMP
EMPNO ENAME SAL
Using FORALL and BULK COLLECT copy data from EMP TO EMP_TEMP
DECLARE
type empno_array is table of emp.empno%type
index by binary_integer;
type ename_array is table of emp.ename%type
index by binary_integer;
type sal_array is table of emp.sal%type
index by binary_integer;
e empno_array;
n ename_array;
s sal_array;
t1 number;
66
t2 number;
BEGIN
select empno,ename,sal bulk collect into e,n,s from emp;
t1 := dbms_utility.get_time;
/* using FOR loop */
FOR i in e.first..e.last
loop
insert into emp_temp values(e(i),n(i),s(i));
end loop;
t2 := dbms_utility.get_time;
dbms_output.put_line('using FOR loop :-'||to_char(t2-t1));
execute immediate 'truncate table emp_temp';
t2 := dbms_utility.get_time;
/* using FORALL loop */
FORALL i in e.first..e.last
insert into emp_temp values(e(i),n(i),s(i));
t2 := dbms_utility.get_time;
dbms_output.put_line('using FORALL loop :-'||to_char(t2-t1));
END;
/
67
BEGIN
SELECT EMPNO BULK COLLECT INTO E FROM EMP;
FORALL I in E.FIRST..E.LAST
DELETE FROM EMP WHERE EMPNO=E(I);
END;
SQL%BULK_ROWCOUNT:-
The SQL%BULK_ROWCOUNT cursor attribute gives information about the rows
affected by each iteration of the FORALL statement.
Example :-
DECLARE
TYPE etype IS TABLE OF number(2);
e etype := etype(10,20,30,40);
BEGIN
/* PERFORM BULK DELETE OPERATION */
FORALL i IN e.FIRST..e.LAST
DELETE FROM emp WHERE deptno = e(i);
/* ROWS AFFECTED */
FOR i IN e.FIRST..e.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(‘ Deptno = ‘||e(i)||’ ‘||
’Rows affected = ‘||SQL%BULK_ROWCOUNT(i));
END LOOP;
END;
OUTPUT :-
Deptno =10 Rows affected = 3
Deptno = 20 Rows affected = 5
Deptno = 30 Rows affected = 6
Deptno = 40 Rows affected = 0
SAVE EXCEPTIONS and SQL%BULK_EXCEPTION :-
We saw how the FORALL syntax allows us to perform bulk DML operations, but what
happens if one of those individual operations results in an exception? If there is no
exception handler, all the work done by the current bulk operation is rolled back. If
there is an exception handler, the work done prior to the exception is kept, but no
more processing is done. Neither of these situations is very satisfactory, so instead we
should use the SAVE EXCEPTIONS clause to capture the exceptions and allow us to
continue past them. We can subsequently look at the exceptions by referencing the
SQL%BULK_EXCEPTION cursor attribute.
To see this in action create the following table :-
SQL>CREATE TABLE exception_test ( id NUMBER(10) NOT NULL);
The following code creates a collection with 100 rows, but sets the value of rows 50
and 51 to NULL. Since the above table does not allow nulls, these rows will result in an
68
exception. The SAVE EXCEPTIONS clause allows the bulk operation to continue past
any exceptions, but if any exceptions were raised in the whole operation, it will jump to
the exception handler once the operation is complete. In this case, the exception
handler just loops through the SQL%BULK_EXCEPTION cursor attribute to see what
errors occured.
DECLARE
TYPE t_tab IS TABLE OF exception_test.ID%TYPE;
l_tab t_tab := t_tab();
l_error_count NUMBER;
BEGIN
-- Fill the collection.
FOR i IN 1 .. 100
LOOP
l_tab.extend;
l_tab(l_tab.last) := i;
END LOOP;
-- cause failure
l_tab(50) := NULL;
l_tab(51) := NULL;
-- perform bulk insert
FORALL i IN l_tab.first .. l_tab.last SAVE EXCEPTIONS
INSERT INTO exception_test VALUES (l_tab(i));
EXCEPTION
WHEN others THEN
l_error_count := SQL%BULK_EXCEPTIONS.count;
DBMS_OUTPUT.put_line('Number of failures: ' || l_error_count);
FOR i IN 1 .. l_error_count LOOP
DBMS_OUTPUT.put_line('Error: ' || i ||
' Array Index: ' || SQL%BULK_EXCEPTIONS(i).error_index ||
' Message: ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
END LOOP;
END;
PL/SQL RECORDS :-
Table records
Cursor records
User defined records
Table Records :-
69
A table record is declared as follows
E EMP%ROWTYPE;
Cursor Recorrds :-
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),
70
GRADE NUMBER(2)
);
TYPE arrtype IS TABLE OF rectype INDEX BY BINARY_INTEGER;
R rectype;
BEGIN
SELECT E.EMPNO,E.ENAME,E.SAL,
D.DNAME,D.LOC,
S.GRADE BULK COLLECT
INTO R
FROM EMP E, DEPT D, SALGRADE S
WHERE E.DEPTNO = D.DEPTNO AND E.SAL BETWEEN S.LOSAL AND S.HISAL;
FOR I IN R.FIRST..R.LAST
LOOP
DBMS_OUTPUT.PUT_LINE(R(I).EMPNO||’ ‘||R(I).ENAME||’ ‘||R(I).DNAME||’ ‘||
R(I).LOC||’ ‘||R(I).GRADE);
END LOOP;
END;
Assigning Values to Tables with Complex Data Types
DECLARE
TYPE emp_name_rec is RECORD (
ename emp.ename%TYPE,
sal emp.sal%TYPE,
hiredate emp.hiredate%TYPE
);
-- Table type that can hold information about employees
TYPE EmpList_tab IS TABLE OF emp_name_rec;
SeniorSalespeople EmpList_tab;
-- Declare a cursor to select a subset of columns.
CURSOR c1 IS SELECT ename,sal,hiredate
FROM emp;
EndCounter NUMBER := 10;
TYPE EmpCurTyp IS REF CURSOR;
emp_cv EmpCurTyp;
BEGIN
OPEN emp_cv FOR SELECT ename,sal,hiredate
FROM emp
WHERE job = 'SALESMAN' ORDER BY hiredate;
FETCH emp_cv BULK COLLECT INTO SeniorSalespeople;
CLOSE emp_cv;
71
-- for this example, display a maximum of ten employees
IF SeniorSalespeople.LAST > 0 THEN
IF SeniorSalespeople.LAST < 10 THEN
EndCounter := SeniorSalespeople.LAST;
END IF;
FOR i in 1..EndCounter LOOP
DBMS_OUTPUT.PUT_LINE
(SeniorSalespeople(i).ename|| ', '
|| SeniorSalespeople(i).sal|| ', ' || SeniorSalespeople(i).hiredate);
END LOOP;
END IF;
END;
/
Exception Handling in PL/SQL:-
Runtime errors are called Exceptions. If at any time an error occurs in the PL/SQL block
at that point pl/sql block execution is stopped and oracle returns error message. To
continue the program execution and to display user friendly message exception needs
to be handled , to handle exception include EXCEPTION block in PL/SQL program as
follows
DECLARE
<VARIABLES> ;
BEGIN
EXECUTABLE STATEMENTS;
EXCEPTION
END;
72
WHEN exception2 THEN -- another handler for exception2
sequence_of_statements2
WHEN OTHERS THEN -- optional handler for all other errors
sequence_of_statements3
END;
To catch raised exceptions, you write exception handlers. Each handler consists of a
WHEN clause, which specifies an exception, followed by a sequence of statements to be
executed when that exception is raised. These statements complete execution of the
block or subprogram; control does not return to where the exception was raised. In
other words, you cannot resume processing where you left off. The optional OTHERS
exception handler, which is always the last handler in a block or subprogram, acts as
the handler for all exceptions not named specifically. Thus, a block or subprogram can
have only one OTHERS handler. Use of the OTHERS handler guarantees that no
exception will go unhandled.If you want two or more exceptions to execute the same
sequence of statements, list the exception names in the WHEN clause, separating them
by the keyword OR, as follows:
EXCEPTION
If any of the exceptions in the list is raised, the associated sequence of statements is
executed. The keyword OTHERS cannot appear in the list of exception names; it must
appear by itself. You can have any number of exception handlers, and each handler
can associate a list of exceptions with a sequence of statements. However, an
exception name can appear only once in the exception-handling part of a PL/SQL block
or subprogram.In PL/SQL exceptions are two types
73
74
NO_DATA_FOUND example :-
DECLARE
V_ENAME VARCHAR2(20);
V_SAL NUMBER(10);
BEGIN
SELECT ENAME ,SAL INTO V_ENAME,V_SAL FROM EMP
WHERE EMPNO=&NO;
DBMS_OUTPUT.PUT_LINE(V_ENAME||' '||V_SAL);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('no such employee’);
END;
/
TOO_MANY_ROWS example :-
DECLARE
V_SAL NUMBER(10);
BEGIN
SELECT SAL INTO V_SAL FROM EMP;
DBMS_OUTPUT.PUT_LINE(V_SAL);
EXCEPTION
WHEN TOO_MANY_ROWS THEN
75
DBMS_OUTPUT.PUT_LINE('Fetching More than One row’);
END;
OUTPUT:-
Fetching More than One row
Example:-
DECLARE
VEMPNO NUMBER(5);
VNAME VARCHAR2(20);
VDEPTNO NUMBER(2);
BEGIN
SELECT EMPNO,ENAME,DEPTNO INTO VEMPNO,VNAME,VDEPTNO FROM EMP
WHERE EMPNO=7788;
DBMS_OUTPUT.PUT_LINE('THE SCOTT WORKS IN DEPTNO:'||VDEPTNO);
SELECT EMPNO,ENAME,DEPTNO INTO VEMPNO,VNAME,VDEPTNO FROM EMP
WHERE DEPTNO=10;
DBMS_OUTPUT.PUT_LINE(VNAME);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('No such Employee’);
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE('Fetching More than One row’);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(‘Unknown Error’);
END;
/
OUTPUT:-
THE SCOTT WORKS IN DEPTNO:20
ERROR:MORE THAN ONE EMPLOYEE WORKS IN DEPNTO 10
Zero_divide Example :-
DECLARE
A NUMBER(10);
B NUMBER(10);
C NUMBER(10);
BEGIN
A:=&a;
B:=&b;
C:=A/B;
DBMS_OUTPUT.PUT_LINE(C);
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE('divide by zero error');
END;
/
Input :-
A := 10
B := 0
Output :-
76
Divide by zero error
Invalid_cursor Example :-
DECLARE
CURSOR C1 IS SELECT *FROM EMP WHERE SAL>2000;
I EMP%ROWTYPE;
BEGIN
LOOP
FETCH C1 INTO I;
EXIT WHEN C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(I.ENAME||' '||I.SAL);
END LOOP;
CLOSE C1;
EXCEPTION
WHEN INVALID_CURSOR THEN
DBMS_OUTPUT.PUT_LINE('First Open the Cursor');
END;
/
OUTPUT:
DECLARE
CURSOR C1 IS SELECT *FROM EMP WHERE SAL>2000;
I EMP%ROWTYPE;
BEGIN
OPEN C1;
LOOP
FETCH C1 INTO I;
EXIT WHEN C1%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(I.ENAME||' '||I.SAL);
END LOOP;
OPEN C1;
EXCEPTION
WHEN CURSOR_ALREADY_OPEN THEN
DBMS_OUTPUT.PUT_LINE('WE MUST CLOSE THE CURSOR BEFOR REOPENING');
END;
/
invalid_number :-
When we are try to convert string type to number type oracle server returns
INVALID_NUMBER exception.
Example:-
BEGIN
INSERT INTO EMP(EMPNO,SAL)VALUES(1,'ABC');
EXCEPTION
WHEN INVALID_NUMBER THEN
DBMS_OUTPUT.PUT_LINE('INSERTING APPROPRIATE DATA');
END;
77
/
value_error Example :-
DECLARE
Z NUMBER(10);
BEGIN
Z:='&X'+'&Y';
DBMS_OUTPUT.PUT_LINE(Z);
EXCEPTION
WHEN VALUE_ERROR THEN
DBMS_OUTPUT.PUT_LINE('ENTER PROPER DATA');
END;
value error also occurs when we try to store more data than the specified data type
size in a variable
Example :-
DECLARE
Z VARCHAR2(3);
BEGIN
Z:='ABCD';
EXCEPTION
WHEN VALUE_ERROR THEN
DBMS_OUTPUT.PUT_LINE('INVALID STRING LENGTH');
END;
OUTPUT:
Example:-
DECLARE
V_EMPNO EMP.EMPNO%TYPE:=&EMPNO;
V_ENAME EMP.ENAME%TYPE:=&ENAME;
V_DEPTNO EMP.DEPTNO%TYPE:=&DEPTNO;
BEGIN
INSERT INTO EMP(EMPNO,ENAME,DEPTNO)
VALUES(V_EMPNO,V_ENAME,V_DEPTNO);
COMMIT;
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
DBMS_OUTPUT.PUT_LINE(‘Employee Already Exists’);
END;
PL/SQL provides following built-in functions which are used in error handling.
SQLCODE
SQLERRM
SQLCODE returns ERROR CODE
78
SQLERRM returns ERROR MESSAGE
Example :-
DECLARE
X NUMBER;
Y NUMBER;
Z NUMBER;
BEGIN
X := &X;
Y := &Y;
Z := X/Y;
DBMS_OUTPUT.PUT_LINE(Z);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLCODE);
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
Programmer Defined Exceptions :-
These EXCEPTIONS are defined by user and must be raised by user by using
1 RAISE statement
Syntax :-
RAISE <exception-name>;
Example :-
DECLARE
X NUMBER(2);
Y NUMBER(2);
Z NUMBER(2);
ONE_DIVIDE EXCEPTION;
BEGIN
X := &X;
Y := &Y;
IF Y=1 THEN
RAISE ONE_DIVIDE;
END IF;
79
Z := X/Y;
DBMS_OUTPUT.PUT_LINE(Z);
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE(‘ZERO DIVIDE ERROR’);
WHEN ONE_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE(‘ONE DIVIDE ERROR’);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
Using RAISE_APPLICATION_ERROR :-
Syntax:-
Example :-
DECLARE
X NUMBER(2);
Y NUMBER(2);
Z NUMBER(2);
BEGIN
X := &X;
Y := &Y;
IF Y=1 THEN
RAISE_APPLICATION_ERROR(-20001,’ONE DIVIDE ERROR’);
END IF;
Z := X/Y;
DBMS_OUTPUT.PUT_LINE(Z);
EXCEPTION
WHEN ZERO_DIVIDE THEN
DBMS_OUTPUT.PUT_LINE(‘ZERO DIVIDE ERROR’);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
END;
PRAGMA EXCEPTION_INIT :-
80
It is the way to associate user defined exception with oracle Predefined error.If the
oracle predefined error is not having name then we can assign name to that error with
the help of PRAGMA EXCEPTION_INIT.
Syntax :-
Example :-
DECLARE
VDNO DEPT.DEPTNO%TYPE;
child_exists EXCEPTION;
PRAGMA EXCEPTION_INIT(child_exists,-2091);
BEGIN
VDNO := &DEPTNO;
EXCEPTION
END;
Input :- Deptno :- 10
If at least one employees work for 10 th dept then DELETE statement causes
child_exists exception.
Ename varchar2(20) ,
DECLARE
VENO EMP.EMPNO%TYPE;
VENAME EMP.ENAME%TYPE;
VSAL EMP.SAL%TYPE;
CHECK_VIOLATED EXCEPTION;
PRAGMA EXCEPTION_INIT(CHECK_VIOLATED,-2290);
81
BEGIN
VENO := &ENO;
VENAME := '&ENAME';
VSAL := &SAL;
COMMIT;
EXCEPTION
END;
After executing the above program if user enters sal less than 3000 sal then insert
statement causes check_violated exception.
82
Propogation Rules Example 2 :-
83
Example :-
BEGIN
DECLARE
X NUMBER(4) := &X;
BEGIN
DBMS_OUTPUT.PUT_LINE(X);
EXCEPTION
DBMS_OUTPUT.PUT_LINE('SIZE MISMATCH');
END;
EXCEPTION
DBMS_OUTPUT.PUT_LINE('VALUE ERROR');
END;
In the above example if entered value for X is 10000 then the statement causes
VALUE_ERROR exception but that cannot be handled by inner exeception block so it is
propagated to outer block, outer block will handle that exception prints message
VALUE ERROR.
84
Errors occur during runtime processing due to either hardware or network failure or
application logic errors are known as exception.
User-defined exceptions
User defined exception in oracle are defined by the user. They need to be explicitly
raised by the user using the RAISE statement. Oracle is not aware of these exceptions
Explain the guidelines for Avoiding and Handling PL/SQL Errors and
Exceptions.
• Use both error checking and exception handling to ensure your program can
handle all possibilities.
• Add error-checking code whenever you can predict that an error might occur if
your code gets bad input data.
• Make your programs robust enough to work even if the database is not in the
state you expect.
• Test your code with different combinations of bad data to see what potential
errors arise.
85
Pragma Init Exception is declared using the following syntax:-
This directs the complier to add the user defined error to the oracle error number. It
associate the user defined error to predefined error codes.
What is Raise_application_error?
SQLCODE returns the latest code of the error that has occured.
No.
Cursor_Already_Open, Invalid_Cursor
Can the PL/SQL block process more than one exception at a time?
No, the PL/SQL block cannot handle more than one exception. It can process only one
exception handler before terminating.
No, it is not possible to have more than one OTHERS clause in the exception section.
This exception is raised when the program attempts to perform an illegal operation,
such as closing an unopened cursor.
The OTHERS exception handler ensures that no exception goes unhanded and the
program terminates successfully.
This exception is raised when the program tries to open an already opened cursor.
The cursor should be closed before it can be opened again.
86
What is the advantage of having a separate exception section within a
PL/SQL code?
The advantages of having a separate exception section within a PL/SQL code are as
follows:
If an exception is raised and there is no handler in the current block, the exception
would propagate to the enclosing block. If no handler is found in the enclosing block,
the exception will be propagated to the calling environment as an unhandled exception.
RAISE is used to call pre-defined exceptions declared in the declarative section of the
PL/SQL block.
Can processing be resumed from the point exception was raised after the
exception is handled?
After the exception is handled, processing cannot be resumed within the executable
section of the current block from where the exception was raised. The current block
where the exception handier is declared will be terminated. The control will pass to the
statement in the enclosing executable block. If there is no enclosing block, control will
pass back to the calling host environment.
PL/SQL subprograms :-
A PL/SQL subprogram is a named PL/SQL block that can be invoked with a set of
parameters. A subprogram can be either a procedure or a function. Typically, you use a
procedure to perform an action and a function to compute and return a value.
87
A subprogram created at schema level is a standalone stored subprogram. You
create it with the CREATE PROCEDURE or CREATE FUNCTION statement. It is
stored in the database until you drop.
Like every PL/SQL block, a subprogram has an optional declarative part, a required
executable part, and an optional exception-handling part
88
The exception-handling part of a subprogram contains code that handles run-time
errors.
Example :-
declares and defines a function (at the same time) inside an anonymous block. The
function has the optional declarative part and the required executable part, but not the
optional exception-handling part. The executable part of the anonymous block invokes
the function.
DECLARE
AS
original_squared NUMBER;
BEGIN
END;
BEGIN
END;
In a block, you can create multiple nested subprograms. If they invoke each other, you
need forward declaration, because a subprogram must be declared before it can be
invoked. With forward declaration, you declare a subprogram, but do not define it until
89
after you have defined the other subprograms that invoke it. A forward declaration and
its corresponding definition must appear in the same block.
DECLARE
BEGIN
proc1(number2);
END;
-- Define proc 1:
BEGIN
proc2 (number1);
END;
BEGIN
NULL;
END;
Formal parameters are the variables declared in the subprogram header and
referenced in its execution part. Actual parameters are the variables or expressions
that you pass to the subprogram when you invoke it. Corresponding formal and actual
parameters must have compatible data types.A good programming practice is to use
different names for formal and actual parameters
DECLARE
emp_num NUMBER(6) := 120;
bonus NUMBER(6) := 100;
90
merit NUMBER(4) := 50;
PROCEDURE raise_salary (
emp_id NUMBER, -- formal parameter
amount NUMBER -- formal parameter
) IS
BEGIN
UPDATE employees
SET salary = salary + amount
WHERE employee_id = emp_id;
END raise_salary;
BEGIN
raise_salary(emp_num, bonus); -- actual parameters
raise_salary(emp_num, merit + bonus); -- actual parameters
END;
/
When you invoke a subprogram, PL/SQL evaluates each actual parameter and assigns
its value to the corresponding formal parameter. If necessary, PL/SQL implicitly
converts the data type of the actual parameter to the data type of the corresponding
formal parameter before the assignment (this is why corresponding formal and actual
parameters must have compatible data types).
IN
OUT
IN OUT
IN parameter :-
It is default.
91
OUT parameter :-
IN OUT parameter :-
DECLARE
emp_last_name VARCHAR2(25);
BEGIN
FROM employees
END raise_salary;
BEGIN
DBMS_OUTPUT.PUT_LINE
END;
Note :-
92
Before exiting a subprogram, assign values to all OUT formal parameters. Otherwise,
the corresponding actual parameters will be null. If you exit successfully, PL/SQL
assigns values to the actual parameters. If you exit with an unhandled exception,
PL/SQL does not assign values to the actual parameters.
PL/SQL lets you overload local subprograms, packaged subprograms, and type
methods. You can use the same name for several different subprograms as long as
their formal parameters differ in number, order, or data type family.
Example defines two subprograms with the same name, initialize. The procedures
initialize different types of collections. Because the processing in these two procedures
is the same, it is logical to give them the same name.
You can place the two initialize procedures in the same block, subprogram, package, or
object type. PL/SQL determines which procedure to invoke by checking their formal
parameters. The version of initialize that PL/SQL uses depends on whether you invoke
the procedure with a date_tab_typ or num_tab_typ parameter.
DECLARE
93
TYPE num_tab_typ IS TABLE OF NUMBER INDEX BY PLS_INTEGER;
hiredate_tab date_tab_typ;
sal_tab num_tab_typ;
BEGIN
tab(i) := SYSDATE;
END LOOP;
END initialize;
BEGIN
tab(i) := 0.0;
END LOOP;
END initialize;
BEGIN
END;
Restrictions on Overloading:-
Standalone subprograms
PACKAGE pkg IS
PROCEDURE X (p IN VARCHAR2);
END pkg;
PACKAGE pkg IS
PROCEDURE X (p INTEGER);
PROCEDURE X (p REAL);
94
END pkg;
INTEGER and REAL are subtypes of NUMBER, so they belong to the same data type
family.
Functions that differ only in return value data type, even if the data types are in
different families; for example:
PACKAGE pkg IS
END pkg;
Stored Procedures:-
Syntax:-
<Variable Declarations>;
<Constant Declarations>;
BEGIN
EXCEPTION
END;
Example:-
SQL>ed proc1 ;
95
IS
BEGIN
UPDATE emp SET sal = sal + amt WHERE empno = e ;
END;
To create procedure :-
SQL>@proc1 ;
To execute procedure :-
After creating procedure , procedure can be executed from SQL prompt,another PL/SQL
block and from any front-end application Java or .net Aplications
SQL>EXECUTE raise_salary(7844,1000);
DECLARE
veno emp.empno%type;
vamt number(4);
BEGIN
veno := &eno;
vamt := &amount;
raise_salary(veno,vamt); /* call to the procedure */
END;
Using OUT parameter :-
An OUT parameter lets you return values to the caller of a subprogram. Inside the
subprogram, an OUT parameter acts like a variable. That means you can use an OUT
formal parameter as if it were a local variable. You can change its value or reference
the value in any way, as the following example shows:
96
because the third parameter should not be a constant or expression , it should be a
variable.
Calling block:-
Write a pl/sql block to increment particular employee salary by particular amount after
increment if salary exceeds 5000 then cancel that increment ?
DECLARE
veno emp.empno%type;
vamt number(4);
vsal emp.sal%type;
BEGIN
veno := &eno;
vamt := &amount;
raise_salary(veno,vamt,vsal); /* call to the procedure */
if vsal >5000 then
rollback;
else
commit;
end if;
END;
Positional Versus Named Notation for Parameters:-
When calling a subprogram, you can write the actual parameters using either positional
or named notation. That is, you can indicate the association between an actual and
formal parameter by position or name.
DECLARE
acct INTEGER;
amt REAL;
BEGIN
acct := &account;
amt := &amount;
credit_acct(acct, amt); -- positional notation
credit_acct(amount => amt, acct_id => acct); -- named notation
credit_acct(acct_id => acct, amount => amt); -- named notation
credit_acct(acct, amount => amt); -- mixed notation
END;
Using positional notation :-
97
The first procedure call uses positional notation. The PL/SQL compiler associates the
first actual parameter, acct, with the first formal parameter, acct_id. And, the compiler
associates the second actual parameter, amt, with the second formal parameter,
amount.
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');
98
The first call passes no actual parameters, so both default values are used. The second
call passes one actual parameter, so the default value for new_loc is used. The third
call passes two actual parameters, so neither default value is used.
Usually, you can use positional notation to override the default values of formal
parameters. However, you cannot skip a formal parameter by leaving out its actual
parameter. For example, the following call incorrectly associates the actual parameter
'NEW YORK' with the formal parameter new_dname:
create_dept('NEW YORK'); -- incorrect
You cannot solve the problem by leaving a placeholder for the actual parameter. For
example, the following call is not allowed:
create_dept( , 'NEW YORK'); -- not allowed
In such cases, you must use named notation, as follows:
create_dept(new_loc => 'NEW YORK');
PRAGMA AUTONOMOUS_TRANSACTION :-
begin
99
update emp set sal=sal+1000 where empno=7844; MT begins
raise_salary(7566,1000) ; AT committed
end;
result:-
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
DECLARE
Vdno dept.deptno%type;
100
c SYS_REFCURSOR;
r emp%rowtype;
BEGIN
vdno := &deptno;
getEmployeeList(vdno,c);
LOOP
FETCH c into r;
EXIT WHEN c%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(r.ename|| ‘ ‘||r.sal);
END LOOP;
CLOSE c;
END;
Passing Large Data Structures with the NOCOPY Compiler Hint:-
Starting with oracle 8i , PL/SQL offers an option for definition of parameters th NOCOPY
Clause. NOCOPY is a hint to the compiler about how you would like the PL/SQL engine
to work with the data being passed as an OUT or IN OUT parameters .There are two
ways to pass parameter values 1 by reference 2 by value
By Reference :-
By Value :-
When an actual parameter is passed by value ,the value of the actual parameter is
copied to the corresponding formal parameter. If the program then terminates without
an exception, the formal parameter value is copied back to the actual parameter. If an
error occurs , the changed values are not copied back to the actual parameter.
Parameter passing in PL/SQL without the use of NOCOPY follows these rules
IN By Reference
OUT By Value
IN OUT By Value
By default, the OUT and IN OUT parameters are passed by value. That is, the value of
OUT and IN OUT actual parameter is copied into the corresponding formal parameter.
101
Then, if the subprogram exits normally, the values assigned to the OUT and IN OUT
formal parameters are copied into the corresponding actual parameters.
When the parameters hold large data structures such as collections, records, and
instances of object types, all this copying slows down execution and uses up memory.
To prevent that, you can specify the NOCOPY hint, which allows the PL/SQL compiler to
pass OUT and IN OUT parameters by reference.
NOCOPY can improve the performance of programs with IN OUT or OUT parameters
but if program terminates with an unhandled exception you cannot trust the values in a
NOCOPY actual parameter. Use NOCOPY hint when you know that you have a
performance problem relating to your parameter passing
Although PL/SQL is a powerful, flexible language, some tasks are more easily done in
another language. Low-level languages such as C are very fast. Widely used languages
such as Java have reusable libraries for common design patterns.
You can use PL/SQL call specifications to invoke external subprograms written in other
languages, making their capabilities and libraries available from PL/SQL. For example,
you can invoke Java stored procedures from any PL/SQL block, subprogram, or
package.
import java.sql.*;
import oracle.jdbc.driver.*;
102
throws SQLException {
try {
pstmt.setInt(2, empNo);
pstmt.executeUpdate();
pstmt.close();
} catch (SQLException e)
{System.err.println(e.getMessage());}
The class Adjuster has one method, which raises the salary of an employee by a given
percentage. Because raiseSalary is a void method, you publish it as a procedure using
the call specification shown in below Example and then can invoke the procedure
raise_salary from an anonymous PL/SQL block.
AS LANGUAGE JAVA
DECLARE
BEGIN
END;
Stored Functions:-
103
A function is a subprogram that computes a value. Functions and procedures
structured alike except that functions return values using RETURN statement.
Syntax :-
Create a function that accepts account number and should return balance ?
SQL>ED fun1 ;
SQL>@FUN1
Select statement
Sqlprompt
Another pl/sql block
Executing from SELECT stmt:-
Scenario:-
104
CUST_S
CUST_ID CUST_NAME
1 SACHIN RAMESH TENDULKAR
2 MAHINDRA SINGH DHONI
CUST_T
1 Create a function to accept name of the person and should return first_name :-
105
4 Create procedure to copy data from CUST_S to CUST_T :-
PRODUCTS ORDERS
106
RETURN vtbill;
END;
Table Functions:-
Table functions are functions that produce a collection of rows (either a nested table or
a varray) that can be queried like a database table. You use a table function like the
name of a database table, in the FROM clause of a query.
Execution of a table function can be parallelized, and returned rows can be streamed
directly to the next process without intermediate staging. Rows from a collection
returned by a table function can also be pipelined—that is, iteratively returned as they
are produced instead of in a batch after all processing of the table function's input is
completed.
107
SQL>select * from TABLE(gen_numbers(3));
COLUMN_VALUE
------------
1
2
3
Using the PL/SQL Function Result Cache:-
The PL/SQL function result caching means for caching the results of PL/SQL functions
in a System global area (SGA), which is available to every session that runs your
application. The caching mechanism is both efficient and easy to use, and it relieves
you of the burden of designing and developing your own caches and cache-
management policies.
To enable result-caching for a function, use the RESULT_CACHE clause. When a result-
cached function is invoked, the system checks the cache. If the cache contains the
result from a previous call to the function with the same parameter values, the system
returns the cached result to the invoker and does not re execute the function body. If
the cache does not contain the result, the system executes the function body and adds
the result (for these parameter values) to the cache before returning control to the
invoker.
Note:-
If function execution results in an unhandled exception, the exception result is not
stored in the cache. The cache can accumulate very many results—one result for every
unique combination of parameter values with which each result-cached function was
invoked. If the system needs more memory, it ages out (deletes) one or more cached
results.
You can specify the database objects that are used to compute a cached result, so that
if any of them are updated, the cached result becomes invalid and must be
recomputed. The best candidates for result-caching are functions that are invoked
frequently but depend on information that changes infrequently or never.
CREATE OR REPLACE FUNCTION get_hire_date
(emp_id NUMBER, fmt VARCHAR) RETURN VARCHAR
RESULT_CACHE RELIES_ON (HR.EMPLOYEES)
IS
date_hired DATE;
BEGIN
SELECT hire_date INTO date_hired
FROM HR.EMPLOYEES
WHERE EMPLOYEE_ID = emp_id;
RETURN TO_CHAR(date_hired, fmt);
END;
/
108
If the result for get_hire_date(7844,’mm/dd/yy’) is already in the result cache, the
result is returned from the cache; otherwise, the result is computed and added to the
cache. Because the RELIES_ON clause specifies HR.EMPLOYEES, any update to
EMPLOYEES invalidates all cached results for get_hire_date relieving you of
programming cache invalidation logic everywhere that EMPLOYEES might change.
Destroying Procedure or Functions:-
Syntax:-
Example:-
Syntax:-
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.
Answer: Currently, the maximum parsed/compiled size of a PL/SQL block is 64K and
the maximum code size is 100K.
A stored procedure is a named PL/SQL block stored in db and performs some operation
over database
109
What are advantages of Stored Procedures?
IN,OUT,IN-OUT parameters.
The Same procedure name is repeated with parameters of different datatypes and
parameters in different positions, varying number of parameters is called overloading
of procedures.
Answer: Yes
Pseudo-columns are not actual columns in a table but they behave like columns. They
are used in SQL statements to retrieve specific information. PL/SQL recognizes pseudo-
columns as a part of SQL statements but they cannot be used directly in a procedural
language.
=> ROWID
=> ROWNUM
=> LEVEL
=> CURRVAL
=> NEXTVAL.
110
When any database object associated with the subprogram changes, the program
needs to be recompiled. When the program is executed the next time, after the object
is changed, the system will automatically perform runtime recompilation.
Explicit datatypes, %TYPE and %ROWTYPE, without size specification can be used for
parameters in a procedure.
DDL commands, such as create, alter, revoke, and grant, cannot be used directly
within the procedural language block. They can only be called using built-in Native
Dynamic SQL or Dynamic SQL Package, which allows the execution of the DDL
statements at runtime.
111
The scope of any procedure that is defined within the PL/SQL block will be limited as it
can be called only within the PL/SQL block and not by any other procedure or calling
environment.
To execute a subprogram owned by another user, the user must be granted either the
EXECUTE privilege on a procedure or function or the EXECUTE ANY PROCEDURE system
privilege.
The AUTHID property affects the name resolution and privilege checking of SQL
statements at runtime; however, it does not affect the compilation, and has no
meaning for blocks that have no code, such as collection types.
• Any procedure may be created with its AUTHID property set to either the
DEFINER or the INVOKER rights.
• If a procedure is created with the DEFINER rights, then the user executing the
procedure need not have an access to database objects, which the procedure is
accessing.
• However, if a procedure is created with the INVOKER rights, then the user must
have access rights to all the objects that the procedure is accessing.
112
What is difference between a PROCEDURE & FUNCTION?
A FUNCTION is alway returns a value using the return statement. A PROCEDURE may
return one or more values through parameters or may not return at all.
calculate_bonus ('A822');
Can you have two functions with the same name in a PL/SQL block ?
Yes.
Can you have two stored functions with the same name ?
No.
No.
OverLoading means an object performing different functions depending upon the no.of
parameters or the data type of the parameters passed to it.
Yes.
Can 2 functions have same name & input parameters but differ only by return
datatype
No.
Yes, there can be multiple RETURN statements within a function but only one is
executed. After the value is returned, the control passes back to the calling
environment and function processing is stopped.
Can BOOLEAN datatype be used in functions that are called from SQL
statements?
No, BOOLEAN datatype cannot be used in functions that are called from SQL
statements.
113
Procedure is used when there are more than one return values however, if there is
only one return value, then a function should be used. Although functions may have
more than one OUT parameters, it is considered as a poor programming style.
What are the different parameter modes, which can be used in the function
specification?
The three different modes, IN, OUT, and IN OUT, can be used in the function
specification. However, it is recommended to use only the IN mode. This is because
functions are supposed to accept values from the calling environment and return a
single value.
Can you invoke a stored function or procedure from the Oracle Forms in the
database?
Yes, a stored procedure or function can be invoked from the Oracle Forms and the
result can be used for further programming.
The parameter list for a function or procedure can be displayed using the DESCRIBE
command, given as follows:
This error is displayed if the function is executed when it is in the invalid state.
The ORA-06575 error indicates that for some reason the function is not valid and needs
to be checked and compiled again.
What are the restrictions on functions that are called within SQL statements?
The functions that are called within SQL statements have the following restrictions:
114
c. They can only take the IN parameter modes. The OUT and IN OUT parameter
modes are not allowed in a function.
d. They can only use valid SQL datatypes, such as NUMBER, VARCHAR2, and DATE.
However, they cannot use PL/SQL datatypes, such as BOOLEAN, RECORD, and TYPE.
h. They cannot have the ALTER SESSION and ALTER SYSTEM commands.
Packages :-
A package is a schema object that groups logically related PL/SQL types, variables, and
subprograms. Packages usually have two parts a specification ("spec") and a body.
The spec holds public declarations, which are visible to stored subprograms and other
code outside the package. You must declare subprograms at the end of the spec after
all other items (except pragmas that name a specific function; such pragmas must
follow the function spec).
The body holds implementation details and private declarations, which are hidden from
code outside the package. Following the declarative part of the package body is the
optional initialization part, which holds statements that initialize package variables and
do any other one-time setup steps.
Declarations for exceptions. Typically, you must be able to reference these from
different subprograms, so that you can handle exceptions within invoked
subprograms.
Declarations for subprograms that invoke each other. You need not worry about
compilation order for packaged subprograms, making them more convenient
than standalone stored subprograms when they invoke back and forth to each
other.
115
Declarations for overloaded subprograms. You can create multiple variations of a
subprogram, using the same names but different sets of parameters.
Variables that you want to remain available between subprogram calls in the
same session. You can treat variables in a package like global variables.
Package Sepcification:-
The package specification contains public declarations. The declared items are
accessible from anywhere in the package and to any other subprograms in the same
schema.
For Example :- FUNCTION factorial(n NUMBER) RETURN NUMBER
That is all the information needed to invoke the function. You need not consider its
underlying implementation (whether it is iterative or recursive for example).
If a spec declares only types, constants, variables, exceptions then the package body
is unnecessary. Only subprograms and cursors have an underlying implementation.
The package body contains the implementation of every cursor and subprogram
declared in the package spec. Subprograms defined in a package body are accessible
outside the package only if their specs also appear in the package spec. If a
subprogram spec is not included in the package spec, that subprogram can only be
invoked by other subprograms in the same package. A package body must be in the
same schema as the package spec.
Advantages of Packages :-
Modularity
Packages let you encapsulate logically related types, items, and subprograms in a
named PL/SQL module. Each package is easy to understand, and the interfaces
between packages are simple, clear, and well defined. This aids application
development.
When designing an application, all you need initially is the interface information in the
package specs. You can code and compile a spec without its body. Then, stored
subprograms that reference the package can be compiled as well. You need not define
the package bodies fully until you are ready to complete the application.
Information Hiding
116
With packages, you can specify which types, items, and subprograms are public
(visible and accessible) or private (hidden and inaccessible). For example, if a package
contains four subprograms, three might be public and one private. The package hides
the implementation of the private subprogram so that only the package (not your
application) is affected if the implementation changes. This simplifies maintenance and
enhancement. Also, by hiding implementation details from users, you protect the
integrity of the package.
Added Functionality
Packaged public variables and cursors persist for the duration of a session. They can be
shared by all subprograms that execute in the environment. They let you maintain data
across transactions without storing it in the database.
Better Performance
When you invoke a packaged subprogram for the first time, the whole package is
loaded into memory. Later calls to related subprograms in the package require no disk
I/O.
Example :-
END emp_bonus;
BEGIN
117
END;
END emp_bonus;
After writing the package, you can develop applications that reference its types, invoke
its subprograms, use its cursor, and raise its exception. When you create the package,
it is stored in the database for use by any application that has execute privilege on the
package.
Consider the following package, named emp_admin. The package specification declares
the following types, items, and subprograms:
Type EmpRecTyp
Cursor desc_salary
Exception invalid_salary
After writing the package, you can develop applications that reference its types,
invoke its subprograms, use its cursor, and raise its exception. When you create the
package, it is stored in the database for use by any application that has execute
privilege on the package.
package_name VARCHAR2(30));
invalid_salary EXCEPTION;
first_name VARCHAR2,
email VARCHAR2,
118
phone_number VARCHAR2,
job_id VARCHAR2,
salary NUMBER,
commission_pct NUMBER,
manager_id NUMBER,
department_id NUMBER)
RETURN NUMBER;
PROCEDURE fire_employee
PROCEDURE fire_employee
END emp_admin;
FROM employees
first_name VARCHAR2,
email VARCHAR2,
phone_number VARCHAR2,
job_id VARCHAR2,
salary NUMBER,
commission_pct NUMBER,
manager_id NUMBER,
119
department_id NUMBER)
BEGIN
new_emp_id := employees_seq.NEXTVAL;
last_name,
first_name,
email,
phone_number,
SYSDATE,
job_id,
salary,
commission_pct,
manager_id,
department_id);
number_hired := number_hired + 1;
|| TO_CHAR(number_hired) );
RETURN new_emp_id;
END hire_employee;
BEGIN
END fire_employee;
BEGIN
END fire_employee;
min_sal NUMBER;
120
max_sal NUMBER;
BEGIN
FROM employees
END sal_ok;
sal NUMBER(8,2);
jobid VARCHAR2(10);
BEGIN
FROM employees
ELSE
RAISE invalid_salary;
END IF;
DBMS_OUTPUT.PUT_LINE
END raise_salary;
emp_rec EmpRecTyp;
BEGIN
OPEN desc_salary;
121
FETCH desc_salary INTO emp_rec;
END LOOP;
CLOSE desc_salary;
RETURN emp_rec;
END nth_highest_salary;
number_hired := 0;
END emp_admin;
DECLARE
new_emp_id NUMBER(6);
BEGIN
'Enrique',
'EBELDEN',
'555.111.2222',
'ST_CLERK',
2500,
.1,
101,
110);
DBMS_OUTPUT.PUT_LINE
EMP_ADMIN.raise_salary(new_emp_id, 100);
TO_CHAR(emp_admin.nth_highest_salary(10).sal) || ',
TO_CHAR(emp_admin.nth_highest_salary(10).emp_id));
emp_admin.fire_employee(new_emp_id);
122
-- you can also delete the newly added employee as follows:
-- emp_admin.fire_employee('EBELDEN');
END;
To make the items public, place them in the package specification. For
example, emp_rec declared in the spec of the package is available for general use.
ACCT_MASTER
ACCT_TRANS
Package specification:-
123
create or replace package bank
as
procedure new_acct(a number,n varchar2,b number);
procedure close_acct(a number);
function getBalance(a number) return number;
procedure credit(a number,amt number);
procedure debit(a number,amt number);
procedure fund_transfer(s number,t number,amt number);
function getTransList(a number,s date, e date) return sys_refcursor;
function check_acct_exist(a number) return Boolean;
end;
package body :-
124
return vtrid;
end getTrid;
procedure credit(a number,amt number)
is
vtrid acct_trans.trid%type;
begin
update acct_master set bal=bal+amt where accno=a;
vtrid := getTrid;
insert into acct_trans values(vtrid,’D’,sysdate,amt,a);
end credit;
procedure debit(a number,amt number)
is
vtrid acct_trans.trid%type;
begin
update acct_master set bal=bal-amt where accno=a;
vtrid := getTrid;
insert into acct_trans values(vtrid,’W’,sysdate,amt,a);
end debit;
procedure fund_transfer(s number, t number, amt number)
is
begin
debit(s,amt);
credit(t,amt);
end fund_transfer;
function getTransList(a number , s date ,e date) return sys_refcursor
is
c1 sys_refcursor
begin
open c1 for select * from acct_trans
where accno=a and tdate between s and e;
return c1;
end getTransList;
function check_acct_exist(a number) return Boolean
is
vname acct_master.name%type;
begin
select name into vname from acct_master where accno=a;
return true;
exception
125
when no_data_found then
return false;
end check_acct_exist;
end;
calling block:-
write a pl/sql block to transfer amount from one account to another account ?
declare
vsaccno acct_master.accno%type;
vtaccno acct_master.accno%type;
vamt acct_trans.tamt%type;
vbal acct_master.bal%type;
begin
vsaccno := &saccno;
vtaccno := &taccno;
vamt := &amount;
if vsaccno = vtaccno then
raise_application_error(-20001,’accts should not be same’);
end if;
if bank.check_acct_exist(vsaccno)=false then
raise_application_error(-20001,’source acct does not exists’);
end if;
if bank.check_acct_exist(vtaccno)=false then
raise_application_error(-20001,’target acct does not exists’);
end if;
vbal := bank.getBalance(vsaccno);
if vamt > vbal then
raise_application_error(-20001,’insufficient balance’);
end if;
bank.fund_transfer(vsaccno,vtaccno,vamt);
exception
when others then
dbms_output.put_line(sqlerrm);
end;
overloading example :-
create or replace package employee
as
procedure getDetails(e number,n out varchar2);
procedure getDetails(e number, n out varchar2,s out number);
126
procedure getDetails(e number, n out varchar2,s out number,d out number);
end;
/
create or replace package employee
as
procedure getDetails(e number, n out varchar2)
is
begin
select ename into n from emp where empno=e;
end getDetails;
procedure getDetails(e number, n out varchar2,s out number)
is
begin
select ename,sal into n,s from emp where empno=e;
end getDetails;
procedure getDetails(e number, n out varchar2,s out number,d out number)
is
begin
select ename,sal,deptno into n,s,d from emp where empno=e;
end getDetails;
end;
The PL/SQL function result caching means for caching the results of PL/SQL functions
in a System global area (SGA), which is available to every session that runs your
application. The caching mechanism is both efficient and easy to use, and it relieves
you of the burden of designing and developing your own caches and cache-
management policies.
To enable result-caching for a function, use the RESULT_CACHE clause. When a result-
cached function is invoked, the system checks the cache. If the cache contains the
result from a previous call to the function with the same parameter values, the system
returns the cached result to the invoker and does not re execute the function body. If
the cache does not contain the result, the system executes the function body and adds
the result (for these parameter values) to the cache before returning control to the
invoker.
Note:-
If function execution results in an unhandled exception, the exception result is not
stored in the cache. The cache can accumulate very many results—one result for every
unique combination of parameter values with which each result-cached function was
127
invoked. If the system needs more memory, it ages out (deletes) one or more cached
results.
You can specify the database objects that are used to compute a cached result, so that
if any of them are updated, the cached result becomes invalid and must be
recomputed. The best candidates for result-caching are functions that are invoked
frequently but depend on information that changes infrequently or never.
-- Package specification
CREATE OR REPLACE PACKAGE department_pks IS
TYPE dept_info_record IS RECORD (average_salary NUMBER,
number_of_employees NUMBER);
-- Function declaration
FUNCTION get_dept_info (dept_id NUMBER) RETURN dept_info_record
RESULT_CACHE;
END department_pks;
/
CREATE OR REPLACE PACKAGE BODY department_pks AS
-- Function definition
FUNCTION get_dept_info (dept_id NUMBER) RETURN dept_info_record
RESULT_CACHE RELIES_ON (EMPLOYEES)
IS
rec dept_info_record;
BEGIN
SELECT AVG(SALARY), COUNT(*) INTO rec
FROM EMPLOYEES
WHERE DEPARTMENT_ID = dept_id;
RETURN rec;
END get_dept_info;
END department_pks;
/
128
DECLARE
dept_id NUMBER := 50;
avg_sal NUMBER;
no_of_emp NUMBER;
BEGIN
avg_sal := department_pks.get_dept_info(50).average_salary;
no_of_emp := department_pks.get_dept_info(50).number_of_employees;
DBMS_OUTPUT.PUT_LINE('dept_id = ' ||dept_id);
DBMS_OUTPUT.PUT_LINE('average_salary = '|| avg_sal);
DBMS_OUTPUT.PUT_LINE('number_of_employees = ' ||no_of_emp);
END;
/
You invoke the function get_dept_info as you invoke any function. For example, the
following call returns the number of employees in department number 10:
department_pks.get_dept_info(10).number_of_employees;
The following call returns only the average salary in department number 10:
department_pks.get_dept_info(10).average_salary;
If the result for get_dept_info(10) is already in the result cache, the result is returned
from the cache; otherwise, the result is computed and added to the cache. Because the
RELIES_ON clause specifies EMPLOYEES, any update to EMPLOYEES invalidates all
cached results for department_pks.get_dept_info, relieving you of programming cache
invalidation logic everywhere that EMPLOYEES might change.
Oracle Supplied Packages :- the following are list of packages supplied by
oracle.these package members can be invoked from PL/SQL,SQL and JAVA.
DBMS_ALERT
DBMS_APPLICATION_INFO
DBMS_AQ
DBMS_AQADM
DBMS_DDL
DBMS_DESCRIBE
DBMS_JOB
DBMS_LOB
DBMS_LOCK
DBMS_OUTPUT
DBMS_PIPE
DBMS_RANDOM
DBMS_ROWID
DBMS_SESSION
DBMS_SHARED_POOL
DBMS_SPACE
129
DBMS_SQL
DBMS_STANDARD
DBMS_SYSTEM
DBMS_TRANSACTION
DBMS_UTILITY
UTL_FILE
UTL_RAW
UTL_REF
Droping package specification :-
The above command drops only package body but not package specification.
Define Package ?
UTL_FILE package
DBMS_SQL package
DBMS_SCHEDULE
DBMS_LOB package.
130
Triggers:-
A trigger is one of the extensively used database objects. A trigger is a named program
unit that is stored in the database and fired (executed) in response to a specified
event. The specified event is associated with either a table, a view a schema, or the
database
Types of Triggers:-
Provide auditing
Modify table data when DML statements are issued against views
• Do not define triggers that duplicate database features. For example, do not
define triggers to reject bad data if you can do the same checking through
constraints.
131
• Although you can use both triggers and integrity constraints to define and
enforce any type of integrity rule, Oracle strongly recommends that you use
triggers to constrain data input only in the following situations:
•To enforce referential integrity when child and parent tables are on different
nodes of a distributed database
•To enforce complex business rules not definable using integrity constraints
• If the logic for your trigger requires much more than 60 lines of PL/SQL code,
put most of the code in a stored subprogram and invoke the subprogram from
the trigger.
• Use triggers only for centralized, global operations that must fire for the
triggering statement, regardless of which user or database application issues the
statement.
Creating Trigger:-
Syntax:-
Parts of Trigger :-
Trigger Event
Trigger Restriction
132
Trigger Action
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.
LEVELS OF 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.
A statement trigger is executed once per the DML operation , irrespective of number
of records affected by DML operation.
Statement trigger should be used when a triggering statement affects rows in a table
but the processing required is completely independent of the number of rows affected.
Timed Trigger:-
133
When defining a trigger it is necessary to specify the trigger timing i.e. specifying
whether to perform the trigger action [i.e. execute Trigger code block] BEFORE or
AFTER the triggering statement. BEFORE and AFTER apply to both row and the
statement triggers.
Before Triggers:-
Indicates that the trigger will be fired before , the INSERT, UPDATE or DELETE
operation. It is generally used when the trigger action determines whether the
triggering statement should be allowed to complete. By using a BEFORE trigger for this
purpose, the unnecessary processing of the triggering statement and its eventual
rollback [incase an exception is raised in the trigger action] can be eliminated.
After Triggers:-
Indicates that the trigger will be fired after, the INSERT, UPDATE or DELETE
operations.The AFTER triggers are very useful when creating audit trail for database
tables.
Correlation Names:-
The data in the last SQL statement can be classified as NEW values and OLD values.
When the user fires an insert statement, the values of the columns included in the
insert statement can be read by using : NEW.columnname.
Similarly, if the user updates a record, the value before the modification can be read
using :OLD.columnname. The new values can be referenced using :NEW.columnname.
If user deletes a record then the record affected by delete can be read by using :OLD
variable.
Example 1:-
Create trigger to not to allow any DML operation on SUNDAY on EMP table
Create a trigger that fires every time the salary of an employee in the Emp table is
updated. This trigger should print the old, new and the difference in the salary after the
modification as:
134
Employee No :- 1
Difference :- 25000
Solution:
1 ROW CREATED
1 ROW CREATED
135
SQL>INSERT INTO EMP(EMPNO,DEPTNO) VALUES(3,10);
1 ROW CREATED
1 ROW CREATED
Example 4 :-
JOBS
INSTEAD OF Triggers:-
136
An INSTEAD OF trigger defined on a view is fired when an INSERT, UPDATE or
DELETE statement is executed against the view.
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
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:-
1 ROW CREATED.
Example 6:-
137
Similarly to allow deletes via view vwEmpdept, use the following trigger:
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
138
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.
A row-level trigger must not query or modify a mutating table. However, correlation
names such as NEW and OLD cannot be accessed by the trigger.
A statement-level trigger must not query or modify a mutating table if the trigger is
fired as result of a CASCADE delete.
Auditing :-
Create a trigger to audit DML operations on EMP table into EMP_AUDIT table.
EMP_AUDIT
139
UNAME OPERATION OPERATION_TIME
SCOTT INSERT ---
SCOTT UPDATE ---
SCOTT DELETE --
CREATE OR REPLACE TRIGGER TRG6
AFTER INSERT OR UPDATE OR DELETE
ON EMP
BEGIN
IF INSERTING THEN
INSERT INTO EMP_AUDIT VALUES(USER,’INSERT’,SYSTIMESTAMP);
ELSIF UPDATING THEN
INSERT INTO EMP_AUDIT VALUES(USER,’UPDATE’,SYSTIMESTAMP);
ELSE
INSERT INTO EMP_AUDIT VALUES(USER,’DELETE’,SYSTIMESTAMP);
END IF;
END;
DDL Triggers:-
A DDL trigger is a database trigger whose triggering event is a Data Defining Language
[DDL] statement i.e. a DDL trigger fires when a DDL statement [such as a CREATE,
ALTER or DROP statement] is executed in the database or a particular schema.
Example 7:-
The DBA desires to keep track of the user and the date on which objects are created or
dropped in the database.
Write a DDL trigger that inserts s record in the DDLAudit table. This table holds:
EventName
ObjectOwner
140
ObjectName
ObjectType
Operator
OperationDate
CREATE OR REPLACE TRIGGER DDLAudit
BEFORE CREATE OR DROP ON SCHEMA
BEGIN
INSERT INTO DDLAuditTrail(ora_Sysevent,Ora_Dict_Obj_Owner,
Ora_Dict_obj_Name, Ora_Dict_Obj_Type, USER, SYSDATE );
END ;
Invoking a Java Subprogram from a Trigger:-
141
Database Level Triggers :-
These Triggers are created on Database and trigger event can be STARTUP or
SHUTDOWN.
Example 9:-
The DBA desires to daily track the user and the date when the database was shutdown.
DBAAuditTrail
USER TIME
BEGIN
END;
SQL>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:-
142
NULL; -- Do something here.
END BEFORE EACH ROW;
AFTER EACH ROW IS
BEGIN
NULL; -- Do something here.
END AFTER EACH ROW;
AFTER STATEMENT IS
BEGIN
NULL; -- Do something here.
END AFTER STATEMENT;
END <TRIGGER-NAME>;
Follows Clause:-
Oracle allows more than one trigger to be created for the same timing point, but it has
never guaranteed the execution order of those triggers. The Oracle 11g trigger syntax
now includes the FOLLOWS clause to guarantee execution order for triggers defined
with the same timing point. The following example demonstrates how follows clause
can be used.
Syntax:-
Example:-
Disabling Trigger :-
143
Triggers can be disabled and enabled by using ALTER command.
USER_TRIGGERS
ALL_TRIGGERS
DBA_TRIGGERS
Triggers are constructs in PL/SQL that need to be just created and associated with a
table. Once they are created, when the table associated with it gets updated due to an
UPDATE, INSERT or a DELETE, the triggers get implicitly fired depending upon the
instructions passed to them
nd trigger type.
Category - (INSERT, DELETE, UPDATE) i.e. which kind of DML statement causes the
trigger to fire.
Timing – (BEFORE or AFTER) i.e. whether the trigger fires before the statement is
executed of after.
Level – (Row or Statement) i.e. whether it fires once for each row affected by trigger
statement or whether it fires once.
144
Triggers can execute every time some field in database is updated. If a field is likely to
be updated often, it is a system overhead. It is not possible to track or debug triggers.
• Triggers are used for insertions, update and deletions on tables while stored
procedures are often using independently in the database.
Answer:
A trigger if specified FOR EACH ROW; it is fired for each of the table being affected by
the triggering statement. For example if a trigger needs to be fired when rows of a
table are deleted, it will be fired as many times the rows are deleted.
If FOR EACH ROW is not specified, it is application to a statement and the trigger is
executed at a statement level.
At times when SQL statement of a trigger can fire other triggers. This results in
cascading triggers. Oracle allows around 32 cascading triggers. Cascading triggers can
cause result in abnormal behavior of the application
How many types of database triggers can be specified on a table? What are
they?
145
Is it possible to use Transaction control Statements such a ROLLBACK or
COMMIT in Database Trigger? Why?
It is not possible. As triggers are defined for each table, if you use COMMIT of
ROLLBACK in a trigger, it affects logical transaction processing.
Yes, the USER_OBJECTS view has one row entry for each trigger in the schema.
What are the system privileges that are required by a schema owner (user)
to create a trigger on a table?
A user must be able to alter a table to create a trigger on the table. The user must
own the table and either have the ALTER TABLE privilege on that table or have the
ALTER ANY TABLE system privilege. In addition, the user must have the CREATE
TRIGGER system privilege. User should have the CREATE ANY TRIGGER system
privilege to be able to create triggers in any other user account or schema.
146
A database-level event trigger can be created if the user has the ADMINISTER
DATABASE TRIGGER system privilege.
Trigger that is executed when a database event, such as startup, shutdown, or error,
occurs is called a database event trigger. It can be used to reference the attributes of
the event and perform system maintenance functions immediately after the database
startup.
The USER_ERRORS view can be used to show all the parsing errors that occur in
a trigger during the compilation until they are resolved.
It is good to disable triggers during data load operations. This improves the
performance of the data loading activities. The data modification and manipulation that
the trigger would have performed has to be done manually after the data loading.
Yes, USER_TRIGGERS have entries for all triggers that are created in the schema
with or without errors.
Triggers can be used to track values for data operations on tables. This is done using
the old and new qualifiers within the trigger code. These two clauses help keep track of
the data that is being inserted, updated, or deleted in the table; and therefore,
facilitate in application auditing of DML statements. The audit trail can be written to a
user-defined table and audit records can be generated for both row-level and
statement-level triggers.
Answer:
D. SET PAUSE ON
147
Option D cannot be customized; whereas, all the other options can be customized
using triggers.
The INSTEAD OF triggers are used in association with views. These triggers inform
the database of what actions are to be performed instead of the actions that invoked
the trigger. Therefore, the INSTEAD OF triggers can be used to update the underlying
tables, which are part of the views.
No
Yes, triggers have the capability of stopping any DML statement from execution on a
table. Any logical business rule can be implemented using PL/SQL to block modification
on table data.
No, a SELECT statement cannot fire a trigger. DML statements, such as INSERT,
UPDATE, and DELETE, can cause triggers to fire.
• This will make the trigger fire when that particular column is updated and
therefore, prevents unnecessary action of trigger when other columns are being
updated.
Database triggers are based on system events and can be defined at database or
schema level. The various events on which a database trigger can be based are given
as follows:
148
=> On any specific error that occurs.
• If the trigger code queries this table, then a mutating table error occurs, which
causes the trigger to view the inconsistent data.
• Triggers have restrictions on the usage of large data types as they cannot
declare or reference the LONG and LONG RAW data types and cannot use them even if
they form part of the object with which the trigger is associated.
• Similarly, triggers cannot modify the CLOB and BLOB objects as well; however,
they can reference them for read-only access.
Which data dictionary views have the information on the triggers that are
available in the database?
• The data dictionary views that have information on database triggers are given
as follows:
USER_OBJECTS — Contain the name and status of a trigger as well as the date
Updating 30 million records. The update operation fails after 30 minutes just because
one of the record amongst those 30 million fails a check constraint.
An INSERT AS SELECT command fails on the row number 899 to 1000 just because
one column value is too large
The DML error logging feature allows adding a clause to the INSERT statement that
causes the 999 correct records to be inserted successfully and the one erroneous
record to be written out to a error logging table for resolving later.
Example:-
149
Create a table with a few constraints that can be violated for demonstration purpose.
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;
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,
150
REJECT LIMIT clause is technically optional, the default reject limit is zero. The
error logging clause is inefffective if a reject limit is not specified.
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.
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 ;
151
‘a’ for append
F1 := UTL_FILE.FOPEN(‘DIR1’,’abc.txt’,’r’)
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 ;
DECLARE
F1 UTL_FILE.FILE_TYPE;
BEGIN
F1 := UTL_FILE.FOPEN(‘DIR1’,’abc.txt’,’w’);
UTL_FILE.PUT_LINE(F1,’HELLO’);
UTL_FILE.PUT_LINE(F1,’WELCOME’);
UTL_FILE.PUT_LINE(F1,’TO NARESH TECHNOLOGIES’);
UTL_FILE.FCLOSE(F1);
END;
After executing the above program under D:\NARESHIT directory abc.txt file is created
And 3 lines are added to that text file.
Program to read Data from Text file :-
DECLARE
F1 UTL_FILE.FILE_TYPE;
S VARCHAR2(1000);
BEGIN
F1 := UTL_FILE.FOPEN(‘DIR1’,’abc.txt’,’r’);
LOOP
UTL_FILE.GET_LINE(F1,S);
152
DBMS_OUTPUT.PUT_LINE(S);
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
UTL_FILE.FCLOSE(F1);
END;
HELLO
WELCOME
NARESH IT
Program to write employee data into a text file :-
DECLARE
TYPE ETYPE IS TABLE OF EMP%ROWTYPE;
rec ETYPE;
F1 UTL_FILE.FILE_TYPE;
BEGIN
F1 := UTL_FILE.FOPEN(‘DIR1’,’emp.txt’,’w’);
SELECT * BULK COLLECT INTO rec FROM EMP;
FOR I IN rec.FIRST..rec.LAST
LOOP
UTL_FILE.PUT_LINE(F1,rec(I).empno||’ ,’||rec(I).ename||’,’||rec(I).sal);
END LOOP;
UTL_FILE.FCLOSE(F1);
END;
Program to read data from text file :-
DECLARE
F1 UTL_FILE.FILE_TYPE;
rec varchar2(1000);
BEGIN
F1 := UTL_FILE.FOPEN(‘DIR1’,’emp.txt’,’r’);
LOOP
UTL_FILE.GET_LINE(F1,rec);
DBMS_OUTPUT.PUT_LINE(rec);
END LOOP;
EXCEPTION
WHEN NO_DATA_FOUND THEN
UTL_FILE.FCLOSE(F1);
END;
153
Dynamic SQL :-
1 Native dynamic SQL, a PL/SQL language (that is, native) feature for building and
executing dynamic SQL statements.
2 DBMS_SQL package, an API for building, executing, and describing dynamic SQL
statements
Native dynamic SQL code is easier to read and write than equivalent code that uses the
DBMS_SQL package, and runs noticeably faster. However, to write native dynamic SQL
code, you must know at compile time the number and data types of the input and
output variables of the dynamic SQL statement. If you do not know this information at
compile time, you must use the DBMS_SQL package.
That is, any SQL construct not included in Description of Static SQL.
If you do not need dynamic SQL, use static SQL, which has the following advantages:
Successful compilation verifies that static SQL statements reference valid database
objects and that the necessary privileges are in place to access those
objects.Successful compilation creates schema object dependencies.
Native dynamic SQL processes most dynamic SQL statements by means of the
EXECUTE IMMEDIATE statement.If the dynamic SQL statement is a SELECT statement
that returns multiple rows, native dynamic SQL gives you the following choices:
Use the EXECUTE IMMEDIATE statement with the BULK COLLECT INTO
clause.
154
Using the EXECUTE IMMEDIATE Statement:-
The EXECUTE IMMEDIATE statement is the means by which native dynamic SQL
processes most dynamic SQL statements.
If the dynamic SQL statement is self-contained (that is, if it has no placeholders for
bind arguments and the only result that it can possibly return is an error), then the
EXECUTE IMMEDIATE statement needs no clauses.
If the dynamic SQL statement includes placeholders for bind arguments, each
placeholder must have a corresponding bind argument in the appropriate clause of the
EXECUTE IMMEDIATE statement
If the dynamic SQL statement is a SELECT statement that can return at most one row,
put out-bind arguments (defines) in the INTO clause and in-bind arguments in the
USING clause.
If the dynamic SQL statement is a SELECT statement that can return multiple rows, put
out-bind arguments (defines) in the BULK COLLECT INTO clause and in-bind arguments
in the USING clause.
If the dynamic SQL statement is a DML statement other than SELECT, without a
RETURNING INTO clause, put all bind arguments in the USING clause.
If the dynamic SQL statement is a DML statement with a RETURNING INTO clause, put
in-bind arguments in the USING clause and out-bind arguments in the RETURNING
INTO clause.
If the dynamic SQL statement is an anonymous PL/SQL block or a CALL statement, put
all bind arguments in the USING clause.
dname IN VARCHAR2,
mgrid IN NUMBER,
locid IN NUMBER
) AS
BEGIN
deptid := departments_seq.NEXTVAL;
END;
155
DECLARE
plsql_block VARCHAR2(500);
new_deptid NUMBER(4);
BEGIN
END;
DECLARE
FUNCTION f (x INTEGER)
RETURN BOOLEAN
AS
BEGIN
...
END f;
dyn_stmt VARCHAR2(200);
b1 BOOLEAN;
BEGIN
dyn_stmt := 'BEGIN :b := f(5); END;';
-- Fails because SQL does not support BOOLEAN data type:
EXECUTE IMMEDIATE dyn_stmt USING OUT b1;
END;
Using the OPEN-FOR, FETCH, and CLOSE Statements:-
If the dynamic SQL statement represents a SELECT statement that returns multiple
rows, you can process it with native dynamic SQL as follows:
156
Use an OPEN-FOR statement to associate a cursor variable with the dynamic SQL
statement. In the USING clause of the OPEN-FOR statement, specify a bind argument
for each placeholder in the dynamic SQL statement.
Use the FETCH statement to retrieve result set rows one at a time, several at a time,
or all at once. For syntax see FETCH Statement.
Use the CLOSE statement to close the cursor variable.For syntax see CLOSE
Statement.
Example Native Dynamic SQL with OPEN-FOR, FETCH, and CLOSE Statements :
DECLARE
TYPE EmpCurTyp IS REF CURSOR;
v_emp_cursor EmpCurTyp;
emp_record employees%ROWTYPE;
v_stmt_str VARCHAR2(200);
v_e_job employees.job%TYPE;
BEGIN
-- Dynamic SQL statement with placeholder:
v_stmt_str := 'SELECT * FROM employees WHERE job_id = :j';
-- Open cursor & specify bind argument in USING clause:
OPEN v_emp_cursor FOR v_stmt_str USING 'MANAGER';
-- Fetch rows from result set one at a time:
LOOP
FETCH v_emp_cursor INTO emp_record;
EXIT WHEN v_emp_cursor%NOTFOUND;
END LOOP;
-- Close cursor:
CLOSE v_emp_cursor;
END;
DBMS_SQL package :-
The DBMS_SQL package provides an interface to use dynamic SQL to parse any data
manipulation language (DML) or data definition language (DDL) statement using
PL/SQL. For example, you can enter a DROP TABLE statement from within a stored
procedure by using the PARSE procedure supplied with the DBMS_SQL package.
Subprograms of DBMS_SQL package :-
OPEN_CURSOR:-
To process a SQL statement, you must have an open cursor. When you call the
OPEN_CURSOR Function , you receive a cursor ID number for the data structure
representing a valid cursor maintained by Oracle.
Syntax:-
DBMS_SQL.OPEN_CURSOR RETURN INTEGER;
PARSE :-
157
Every SQL statement must be parsed by calling the PARSE Procedure. Parsing the
statement checks the statement's syntax and associates it with the cursor in your
program. You can parse any DML or DDL statement. DDL statements are run on the
parse, which performs the implicit commit.
Syntax:-
DBMS_SQL.PARSE ( c IN INTEGER,
statement IN VARCHAR2,
language_flag IN INTEGER);
BIND_VARIABLE or BIND_ARRAY:-
Many DML statements require that data in your program be input to Oracle. When you
define a SQL statement that contains input data to be supplied at runtime, you must
use placeholders in the SQL statement to mark where data must be supplied.
EXECUTE:-
Call the EXECUTE function to run your SQL statement.
Syntax:-
DBMS_SQL.EXECUTE ( c IN INTEGER) RETURN INTEGER;
CLOSE_CURSOR:-
When you no longer need a cursor for a session, close the cursor by calling
CLOSE_CURSOR.
Syntax:-
DBMS_SQL.CLOSE_CURSOR ( c IN OUT INTEGER);
The below procedure deletes all of the employees from the EMP table whose salaries
are greater than the salary that you specify when you run procedure
Example 1:-
CREATE OR REPLACE PROCEDURE delete_emp(salary IN NUMBER) AS
cursor_name INTEGER;
rows_processed INTEGER;
BEGIN
cursor_name := dbms_sql.open_cursor;
DBMS_SQL.PARSE(cursor_name, 'DELETE FROM emp WHERE sal > :x',
DBMS_SQL.NATIVE);
DBMS_SQL.BIND_VARIABLE(cursor_name, ':x', salary);
rows_processed := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
EXCEPTION
WHEN OTHERS THEN
DBMS_SQL.CLOSE_CURSOR(cursor_name);
END;
After creating above procedure , it can be invoked as follows
158
SQL>EXECUTE delete_emp(2000);
After executing above procedure , it deletes all employee records earning more than
2000.
Example 2
The following sample procedure is passed a SQL statement, which it then parses and
runs:
CREATE OR REPLACE PROCEDURE exec_DDL(STRING IN varchar2) AS
cursor_name INTEGER;
ret INTEGER;
BEGIN
cursor_name := DBMS_SQL.OPEN_CURSOR;
DBMS_SQL.PARSE(cursor_name, string, DBMS_SQL.NATIVE);
ret := DBMS_SQL.EXECUTE(cursor_name);
DBMS_SQL.CLOSE_CURSOR(cursor_name);
END;
For example, after creating this procedure, you could make the following call:
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');
159
dbms_sql.close_cursor(c1);
END LOOP;
END;
Working With LOBs :-
Starting from Oracle8. Oracle database capable of stroing images, videos, maps all
types of unstructured data.
The LOB data type allows holding and manipulating unstructured data such as texts,
graphic images, video sound files. The dbms_lob package was designed to
manipulate LOB data types. Oracle provides the dbms_lob package which is used to
access and manipulate LOB values in both internal or external storage locations.
With this package dbms_lob, it is possible to read and modify given BLOB,
CLOB and NLOBtypes as well as effecting operations of reading in BFILEs. The types
of data used for package dbms_lob include:
BLOB
RAW
CLOB
VARCHAR2
INTEGER
BFILE
LOBs are two types :-
Internal LOBs (inline): They are the LOBs stored inside of the database in a
way that optimizes performance and supplies an efficient access environment, taking
advantage of Oracle Database security and reliability features. These can be
persistent or temporary types.
160
Public variables Data type Value
lob_readonly INTEGER 0
lob_readwrite INTEGER 1
APPEND procedures
The APPEND procedures provide the capability to append one large object to
another.
COMPARE function
The COMPARE function performs an exact byte-by-byte comparison of two large
objects for a given length at given offsets.
COPY procedures
The COPY procedures provide the capability to copy one large object to another.
ERASE procedures
The ERASE procedures provide the capability to erase a portion of a large object.
GETLENGTH function
The GETLENGTH function returns the length of a large object.
INSTR function
The INSTR function returns the location of the nth occurrence of a specified
pattern within a large object.
READ procedures
The READ procedures provide the capability to read a portion of a large object
into a buffer.
SUBSTR function
The SUBSTR function provides the capability to return a portion of a large object.
TRIM procedures
The TRIM procedures provide the capability to truncate a large object to the
specified length.
WRITE procedures
The WRITE procedures provide the capability to write data into a large object.
Example :-
CREATE TABLE book (
id NUMBER (10) PRIMARY KEY,
isbn CHAR(10 CHAR),
description CLOB,
nls_description NCLOB,
misc BLOB,
chapter_title VARCHAR2(30 CHAR),
bfile_description BFILE);
SQL>INSERT INTO BOOK VALUES (id,isbn,description,nls_description,misc,
bfile_description)VALUES (1,'3', EMPTY_CLOB(),
EMPTY_CLOB(),EMPTY_BLOB(),
161
BFILENAME('book_LOC', 'b.pdf'));
1 row created.
DECLARE
v_dest_blob BLOB;
v_dest_clob CLOB;
v_source_locator1 BFILE := BFILENAME('book_LOC', 'bfile_example.pdf');
v_source_locator2 BFILE := BFILENAME('book_LOC', 'bfile_example.txt');
BEGIN
UPDATE book SET description = EMPTY_CLOB(),misc = EMPTY_BLOB();
SELECT description, misc INTO v_dest_clob, v_dest_blob FROM book
WHERE id = 1 FOR UPDATE;
DBMS_LOB.OPEN(v_source_locator1, DBMS_LOB.LOB_READONLY);
DBMS_LOB.OPEN(v_source_locator2, DBMS_LOB.LOB_READONLY);
DBMS_LOB.OPEN(v_dest_blob, DBMS_LOB.LOB_READWRITE);
DBMS_LOB.OPEN(v_dest_clob, DBMS_LOB.LOB_READWRITE);
DBMS_OUTPUT.PUT_LINE('Length of the BLOB file is: '||
DBMS_LOB.GETLENGTH(v_source_locator1));
DBMS_OUTPUT.PUT_LINE('Length of the CLOB file is: '||
DBMS_LOB.GETLENGTH(v_source_locator2));
DBMS_OUTPUT.PUT_LINE('Size of BLOB pre-load: '||
DBMS_LOB.GETLENGTH(v_dest_blob));
DBMS_OUTPUT.PUT_LINE('Size of CLOB pre-load: '||
DBMS_LOB.GETLENGTH(v_dest_clob));
DBMS_LOB.CLOSE(v_source_locator1);
DBMS_LOB.CLOSE(v_source_locator2);
DBMS_LOB.CLOSE(v_dest_blob);
DBMS_LOB.CLOSE(v_dest_clob);
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(SQLERRM);
DBMS_LOB.CLOSE(v_source_locator1);
DBMS_LOB.CLOSE(v_source_locator2);
DBMS_LOB.CLOSE(v_dest_blob);
DBMS_LOB.CLOSE(v_dest_clob);
END;
/
Copying Object from BFILE to BLOB :-
create or replace procedure
update_cust_photo(d number,f varchar2)
162
is
src bfile;
tgt blob;
x number;
begin
select cphoto into tgt
from cust
where cid=d for update ;
src := bfilename('D55',f);
dbms_lob.open(src,dbms_lob.lob_readonly);
x := dbms_lob.getlength(src);
dbms_lob.loadfromfile(tgt,src,x);
update cust set cphoto = tgt
where cid=d;
commit;
dbms_lob.close(src); end;
Overview of Wrapping:-
Wrapping is the process of hiding PL/SQL source code. Wrapping helps to protect
your source code from business competitors and others who might misuse it.
You can wrap PL/SQL source code with either the wrap utility or DBMS_DDL
subprograms. The wrap utility wraps a single source file, such as a SQL*Plus
script. The DBMS_DDL subprograms wrap a single dynamically generated
PL/SQL unit, such as a single CREATE PROCEDURE statement.
Wrapped source files can be moved, backed up, and processed by SQL*Plus and
the Import and Export utilities, but they are not visible through the static data
dictionary views *_SOURCE.
Guidelines for Wrapping:-
Wrap only the body of a package or object type, not the specification.
This allows other developers to see the information they must use the package
or type, but prevents them from seeing its implementation.
Wrap code only after you have finished editing it.
You cannot edit PL/SQL source code inside wrapped files. Either wrap your code
after it is ready to ship to users or include the wrapping operation as part of your
build environment. To change wrapped PL/SQL code, edit the original source file
and then wrap it again.
Before distributing a wrapped file, view it in a text editor to be sure that
all important parts are wrapped.
Limitations of Wrapping:-
Wrapping is not a secure method for hiding passwords or table names.
Wrapping a PL/SQL unit prevents most users from examining the source
code
163
Wrapping does not hide the source code for triggers.
To hide the workings of a trigger, write a one-line trigger that invokes a
wrapped subprogram.
Wrapping does not detect syntax or semantic errors.
Wrapping PL/SQL Code with wrap Utility:-
The wrap utility processes an input SQL file and wraps only the PL/SQL units in
the file, such as a package specification, package body, function, procedure,
type specification, or type body. It does not wrap PL/SQL content in anonymous
blocks or triggers or non-PL/SQL code.
To run the wrap utility, enter the wrap command at your operating system
prompt using the following syntax
wrap iname = inputfile
input file is the name of a file containing SQL statements, that you typically run
using SQL*Plus. If you omit the file extension, an extension of .sql is assumed.
For example, the following commands are equivalent:
wrap iname=/mydir/myfile
wrap iname=/mydir/myfile.sql
You can also specify a different file extension:
wrap iname=/mydir/myfile.src
output_file is the name of the wrapped file that is created. The defaults to that
of the input file and its extension default is .plb. For example, the following
commands are equivalent:
wrap iname=/mydir/myfile
wrap iname=/mydir/myfile.sql oname=/mydir/myfile.plb
You can use the option oname to specify a different file name and extension:
wrap iname=/mydir/myfile oname=/yourdir/yourfile.out
Input and Output Files for the PL/SQL wrap Utility:-
The input file can contain any combination of SQL statements. Most statements
are passed through unchanged. CREATE statements that define subprograms,
packages, or object types are wrapped; their bodies are replaced by a scrambled
form that the PL/SQL compiler understands.
The following CREATE statements are wrapped:-
CREATE [OR REPLACE] FUNCTION function_name
CREATE [OR REPLACE] PROCEDURE procedure_name
CREATE [OR REPLACE] PACKAGE package_name
CREATE [OR REPLACE] PACKAGE BODY package_name
CREATE [OR REPLACE] TYPE type_name AS OBJECT
CREATE [OR REPLACE] TYPE type_name UNDER type_name
CREATE [OR REPLACE] TYPE BODY type_name
The CREATE [OR REPLACE] TRIGGER statement, and [DECLARE] BEGIN-END
anonymous blocks, are not wrapped. All other SQL statements are passed
unchanged to the output file.
164
All comment lines in the unit being wrapped are deleted, except for those in a
CREATE OR REPLACE header and C-style comments (delimited by /* */).
The output file is a text file, which you can run as a script in SQL*Plus to set up
your PL/SQL subprograms and packages. Run a wrapped file as follows:
SQL> @wrapped_file_name.plb;
Running the wrap Utility
For example, assume that the wrap_test.sql file contains the following:
CREATE PROCEDURE wraptest IS
TYPE emp_tab IS TABLE OF employees%ROWTYPE INDEX BY PLS_INTEGER;
all_emps emp_tab;
BEGIN
SELECT * BULK COLLECT INTO all_emps FROM employees;
FOR i IN 1..10 LOOP
DBMS_OUTPUT.PUT_LINE('Emp Id: ' || all_emps(i).employee_id);
END LOOP;
END;
/
To wrap the file, run the following from the operating system prompt:
wrap iname=wrap_test.sql
The output of the wrap utility is similar to the following:
PL/SQL Wrapper: Release 10.2.0.0.0 on Tue Apr 26 16:47:39 2005
Copyright (c) 1993, 2005, Oracle. All rights reserved.
Processing wrap_test.sql to wrap_test.plb
If you view the contents of the wrap_test.plb text file, the first line is CREATE
PROCEDURE wraptest wrapped and the rest of the file contents is hidden.
You can run wrap_test.plb in SQL*Plus to execute the SQL statements in the
file:
SQL> @wrap_test.plb
After the wrap_test.plb is run, you can execute the procedure that was created:
SQL> CALL wraptest();
Wrapping PL/QL Code with DBMS_DDL Subprograms:-
The DBMS_DDL package contains procedures for wrapping a single PL/SQL unit,
such as a package specification, package body, function, procedure, type
specification, or type body. These overloaded subprograms provide a mechanism
for wrapping dynamically generated PL/SQL units that are created in a database.
The DBMS_DDL package contains the WRAP functions and the
CREATE_WRAPPED procedures. The CREATE_WRAPPED both wraps the text and
creates the PL/SQL unit. When invoking the wrap procedures, use the fully
qualified package name, SYS.DBMS_DDL, to avoid any naming conflicts and the
possibility that someone might create a local package called DBMS_DDL or
define the DBMS_DDL public synonym. The input CREATE OR REPLACE
165
statement executes with the privileges of the user who invokes
DBMS_DDL.WRAP or DBMS_DDL.CREATE_WRAPPED.
Example Using DBMS_DDL.CREATE_WRAPPED Procedure to Wrap a
Package:-
DECLARE
package_text VARCHAR2(32767); -- text for creating package spec & body
FUNCTION generate_spec (pkgname VARCHAR2) RETURN VARCHAR2 AS
BEGIN
RETURN 'CREATE PACKAGE ' || pkgname || ' AS
PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER);
PROCEDURE fire_employee (emp_id NUMBER);
END ' || pkgname || ';';
END generate_spec;
FUNCTION generate_body (pkgname VARCHAR2) RETURN VARCHAR2 AS
BEGIN
RETURN 'CREATE PACKAGE BODY ' || pkgname || ' AS
PROCEDURE raise_salary (emp_id NUMBER, amount NUMBER) IS
BEGIN
UPDATE employees
SET salary = salary + amount WHERE employee_id = emp_id;
END raise_salary;
PROCEDURE fire_employee (emp_id NUMBER) IS
BEGIN
DELETE FROM employees WHERE employee_id = emp_id;
END fire_employee;
END ' || pkgname || ';';
END generate_body;
BEGIN
-- Generate package spec
package_text := generate_spec('emp_actions')
-- Create wrapped package spec
DBMS_DDL.CREATE_WRAPPED(package_text);
-- Generate package body
package_text := generate_body('emp_actions');
-- Create wrapped package body
DBMS_DDL.CREATE_WRAPPED(package_text);
END;
/
-- Invoke procedure from wrapped package
166
CALL emp_actions.raise_salary(120, 100);
When you check the static data dictionary views *_SOURCE, the source is
wrapped, or hidden, so that others cannot view the code details. For example:
SELECT text FROM USER_SOURCE WHERE name = 'EMP_ACTIONS';
The resulting output is similar to the following:
TEXT
--------------------------------------------------------------------
PACKAGE emp_actions WRAPPED
a000000
1f
abcd
167