PLSQL PLSQL Complete
PLSQL PLSQL Complete
Name
: ______________________________________
Batch
: ______________________________________
Copyright 2008
Second Edition
Email:
info@sqlstar.com
No part of this publication may be reproduced (incl. photocopying) in any way, without prior
agreement and written permission of SQL Star International Ltd., Hyderabad. SQL Star
International Ltd., Hyderabad assumes no responsibility for its use, nor for any infringements
of patents or other rights of third parties which could result.
Table of Contents
10g PL /SQL
CHAPTER
No.
CHAPTER TITLE
PAGE
NO
1.
Introduction to PL/SQL
1-25
2.
DMLs in PL/SQL
26-38
3.
Control Structure
39-59
4.
Composite DataTypes
5.
105-128
6.
129-146
7.
Creating subprograms
147-170
8.
Managing subPrograms
171-184
9.
Creating Packages
185-204
10.
205-223
11.
224-250
12.
251-277
13.
Large Objects
278-295
14.
Database Triggers
296-320
15.
321-339
60-104
-3-
Chapter 1
Introduction to PL/SQL
Need for PL/SQL
Benefits of Using PL/SQL
PL/SQL Block Types and Constructs
PL/SQL Block Structure
Operators and SQL Functions in PL/SQL
Variables
Nested Blocks
1
SQL Star International Ltd.
Objectives
At the end of this chapter, you will be able to:
State the need for a procedural language in Oracle
Create PL/SQL blocks
Write nested blocks
2
SQL Star International Ltd.
Introducing PL/SQL
In the journey so far, you have learnt about the Structured Query Language (SQL).
The Oracle software provides a language, Procedural Language/SQL (PL/SQL), which
is an extension of SQL.
PL/SQL implements modularity in the codes you write. It allows you to perform
iterations and handle errors. Features like data hiding and error handling make
PL/SQL a state-of-the-art language for databases.
PL/SQL also allows data manipulation and SQL query statements to be included
within procedural units of code. This makes PL/SQL a powerful transaction processing
mechanism.
New employees or existing employees who are handed over someone elses
work, would have a problem understanding the process followed by
the employee vis--vis the organization
3
SQL Star International Ltd.
Integration
PL/SQL plays a key role in:
Oracle server, through stored procedures and functions, database triggers and
packages
Oracle development tools, through Oracle Developer component triggers
PL/SQL supports the use of SQL datatypes. With direct access provided by SQL,
these shared datatypes integrate PL/SQL with the Oracle server data dictionary.
Hence, PL/SQL successfully bridges the gap between access to database technology
and the need for procedural language capabilities.
Most applications such as Oracle Forms, Oracle Reports and Oracle Graphics, use
shared libraries (that hold code), which can be accessed locally or remotely. To
execute these stored codes, the Oracle tools have their own PL/SQL engine
(independent of the engine present in the Oracle server). The engine first filters out
the SQL statements to send them individually to the SQL statement executor in the
Oracle server. It then processes the remaining procedural statements in the
procedural statement executor in the PL/SQL engine. The procedural statement
executor processes data, which is already inside the client environment and not in
the database. This reduces the workload on the Oracle server and also the amount of
memory required.
4
SQL Star International Ltd.
Portability
PL/SQL is portable. That means, PL/SQL programs can be run wherever the Oracle
server exists. There is no need to tailor them to suit each new environment. This is
achieved because PL/SQL is native to the Oracle server, and therefore can be moved
to any environment (operating system or platform) that supports the Oracle server.
PL/SQL code can also be moved between the Oracle server and Oracle Developer
applications by writing programs and creating libraries in different environments.
5
SQL Star International Ltd.
Variable Declaration
In PL/SQL, you can declare variables:
To use them in SQL and procedural statements
Belonging to different data types
Dynamically based on the structure of tables and columns in the database
6
SQL Star International Ltd.
7
SQL Star International Ltd.
8
SQL Star International Ltd.
END;
/
Some points that will help you write a block:
9
SQL Star International Ltd.
Do not give the same name for the identifiers as the name of columns
in a table used in the block. If so, then the Oracle server assumes that
the table column is being referred.
Identifiers should not be reserved words except when they are used
within double quotes, for instance INSERT.
10
SQL Star International Ltd.
in a code, to enable
*/
Use a semicolon (;) at the end of all SQL and PL/SQL control statements and
the
END keyword.
Do not use semicolons after the keywords DECLARE, BEGIN and EXCEPTION.
END;
/
He is New Jersey Librarys Member!
He is New Jersey Librarys Member!
He is New Jersey Librarys Member !
PL/SQL procedure successfully completed
Operators in PL/SQL
The following operators are supported in PL/SQL:
Arithmetic
Logical
Relational or Comparison
Concatenation
Exponentiation [Represented by (**)]
Date functions
Timestamp functions
DECODE
Group functions like AVG, MIN, MAX, COUNT, SUM, STDDEV and
VARIANCE. These functions work only on a group of rows in a table and
hence, they can be used only with the SQL statements in a PL/SQL block.
Variables
A variable are named memory locations used to store data temporarily. The data
stored in variables is used in the blocks and then processed. When the processing is
completed, the data held in the variables may be written to the database or simply
erased in case of session wide variables.
Why is a Variable used?
A Variable stores data temporarily. It manipulates the stored data and performs
calculations with the data without accessing the database. Once declared, variables
can be used repeatedly in an application by referencing them in other statements in
the block.
12
SQL Star International Ltd.
When variables are declared using %TYPE and %ROWTYPE (more information is
provided later in the chapter), you are infact basing the variable declaration on the
column definition of a table. In this case, if the column definition changes, then the
variable declarations also changes accordingly. This helps in data independence,
reduces maintenance cost and allows the program to adjust to the new business
logic.
How to handle variables in PL/SQL?
In a PL/SQL block, variables are:
Assigned new values in the executable section. On doing so, the existing
value is replaced with the newly assigned value. Care must be taken to see
that a variable being referred to is already declared in the declarative section.
Forward references cannot be made.
Types of Variables
Variables are of two types. They are:
PL/SQL variables
Non-PL/SQL variables
PL/SQL Variables
PL/SQL variables have a data type that specifies a storage format, constraints and
also valid range of values.
The data types used to declare PL/SQL variables are:
Scalar data types are those that correspond to database column types. These
data types hold only a single value. The base scalar data types include:
CHAR
VARCHAR2
LONG
LONG RAW
NUMBER
BINARY_INTEGER
PLS_INTEGER
BINARY_FLOAT
BINARY_DOUBLE
BOOLEAN
DATE
TIMESTAMP
TIMESTAMP WITH TIMEZONE
TIMESTAMP WITH LOCAL TIMEZONE
INTERVAL YEAR TO MONTH
INTERVAL DAY TO SECOND
Binary_Float and Binary_Double are the two new Datatypes introduced in
Oracle10g.They represent floating point numbers in IEEE 754 format (Institute of
13
SQL Star International Ltd.
Electrical and Electronic Engineers) and require 5 byte and 9 bytes to store the
values respectively. IEEE format s supported by most of the computer system
operating through native processor instructions thereby helping us to carry out
complex computations using floating point data.
Composite data types are those that are defined by users. They enable you to
manipulate groups of data in PL/SQL blocks
Reference data types are those that hold values pointing to other objects.
These are also known as pointers.
LOB (large object) data types: are those that hold values, which specify the
location of large objects such as graphic images. These values are known as locators.
Large objects are stored in the same database as the table but not within the table.
LOB data type allows you to store unstructured data of a maximum of 8-128
terabytes. This data could be a movie clip or a graphic image or a sound wave form.
LOBs are further classified into:
14
SQL Star International Ltd.
The following code snippet shows how PL/SQL variables are declared in the
declarative section of a block:
DECLARE
FirstName CHAR(20);
BranchID CHAR(7) NOT NULL: = 09RANNJ;
FeeAmt CONSTANT NUMBER(2): = 15;
CatgName CHAR(15) DEFAULT Fiction;
% TYPE Attribute
If you need to store a database column value in a variable or write a value from a
variable to a database column, then the data type of the variable needs to be the
same as that of the database column. You can use the %TYPE attribute to declare a
variable to be of the same data type as that of a previously declared variable or
database column.
Incorrect variable data types generate PL/SQL errors during execution. When you
want to declare a variable using a table attribute instead of the datatype the syntax
is
<variable_name> table.columnname%TYPE
For example, in case you want to declare a variable that will store the address of a
library member, you will write in the following syntax:
DECLARE
Address VARCHAR2(45);
But, the above declaration will result in an error when an attempt is made to
populate the variable with address details of members from the database table. This
is because there is a mismatch in type specification of the variable. The datatype
width of the vAddress column in table Member is 50, but you have declared it as
45. To overcome this, declare the variable using %TYPE attribute as follows:
DECLARE
Address Member.vAddress%TYPE;
This statement declares a variable whose datatype and width is based on the
vAddress column.
If you want to declare a variable of the same type as a previously declared variable
then the syntax is
<variable_name> variable_name%TYPE
Using %TYPE attribute to declare a variable based on a previously declared variable,
is illustrated in the section dealing with the iSQL*Plus variables within PL/SQL blocks.
15
SQL Star International Ltd.
Datatype and Variable size is determined when the block is compiled. So even if
there is a change in the database column datatype the code manages the changed
datatype information.
payable
is
16
SQL Star International Ltd.
DECLARE
v_Date date:= to_Date( April 04 2007,Month dd YYYY);
BEGIN
DBMS_OUTPUT.PUT_LINE(You have entered
||v_Date || as
input);
END;
/
Example to show the usage of new datatypes.
DECLARE
l_binary_float
l_binary_double
BINARY_FLOAT;
BINARY_DOUBLE;
BEGIN
l_binary_float := 2.1f;
l_binary_double := 2.00001d;
DBMS_OUTPUT.PUT_LINE(l_binary_double);
DBMS_OUTPUT.PUT_LINE(l_binary_float);
l_binary_float := TO_BINARY_FLOAT(2.1);
l_binary_double := TO_BINARY_DOUBLE(2.00001);
DBMS_OUTPUT.PUT_LINE(l_binary_double);
DBMS_OUTPUT.PUT_LINE(l_binary_float);
END;
/
17
SQL Star International Ltd.
Non-PL/SQL Variables
Since PL/SQL has neither input nor output capabilities, it relies on the environment in
which it is executed in order to pass values into and out of a PL/SQL block.
The following non-PL/SQL variables can be used within PL/SQL blocks:
Substitution variables
Host variables
Substitution variables are those that you can use to pass values to a PL/SQL block at
runtime. To reference a substitution variable in a block, prefix it with an ampersand
(&). Before the block is executed the values for the variables are substituted with the
values passed.
Hence, you cannot input different values for the substitution variables using a loop.
The substitution variable can be replaced only by one value.
Here is an example showing the use of substitution variables within PL/SQL blocks.
Suppose, the library wants to calculate its quarterly income based on its annual
income. To do this, it declares a substitution variable, which would prompt the user
to enter the figure of annual income. Based on the value entered, the quarterly
income would be calculated.
DECLARE
AnnualIncome NUMBER (7): = &annualinc;
QuarterlyInc AnnualIncome%TYPE;
-declaring variable based on previously declared variable
using
--%TYPE attribute.
BEGIN
QuarterlyInc:= AnnualIncome/4;
DBMS_OUTPUT.PUT_LINE (The quarterly income of the library
is:
|| QuarterlyInc);
END;
/
In the above block, to display the quarterly income calculated, you can specify the
DBMS_OUTPUT.PUT_LINE in the PL/SQL block. DBMS_OUTPUT is an Oracle supplied
package and PUT_LINE is a procedure within it. To use this you need to specify the
information
you
want
printed,
in
parentheses,
following
the
DBMS_OUTPUT.PUT_LINE command as shown in the above code:
is:
18
SQL Star International Ltd.
QUARTERLYINC
-----------20000
The above example shows the Host variable being assigned a value inside a PL/SQL
block. To assign a value to a host variable outside the Pl/SQL block following can be
done:SQL > VARIABLE QuarterlyInc NUMBER
SQL > Exec :QuarterlyInc := 20000
SQL> PRINT QuarterlyInc
QUARTERLYINC
-----------20000
Where,
19
SQL Star International Ltd.
Nested Blocks
PL/SQL allows blocks to be nested wherever you can have an executable statement.
[This makes the nested block a statement.] Hence, the executable part of a block
can be broken down into smaller blocks. Even the exception section can contain
nested blocks.
Variable Scope
Issues that concern references to identifiers can be resolved taking into account their
scope and visibility.
By scope, we mean that region of a program unit from which an identifier can be
referenced. For instance, a variable in the declarative section can be referenced from
the exception section of that block.
The diagram shown below illustrates the concept of nested blocks and variable
scope.
20
SQL Star International Ltd.
By visibility, we mean the regions from which an identifier can be referenced without
using a qualified name.
The following code snippet shows how to qualify identifiers:
<<outer_blk>> --Block label
DECLARE --This is the parent block
JoiningDt DATE;
BEGIN
DECLARE --his is the child block
JoiningDt DATE;
BEGIN
END;
/
In the code snippet, a variable with the same name as the variable declared in the
outer block is declared in the inner block. To reference the outer block variable in the
inner block, the variable is qualified by prefixing it with the block name.
Identifiers are considered local to the PL/SQL block in which they are declared, and
are considered global to all its sub-blocks. Within the sub-block, only local identifiers
are visible because to reference the global identifiers you must use a qualified name.
If a block cannot find the identifier declared locally, it will look up to declarative
section of the enclosing block (parent block). However, the block will never look
down to the enclosed blocks (child blocks).
Look at the following code and determine the variable scope:
<<OUTER_BLK>>
DECLARE
vSal NUMBER(8,2) := 50000;
vComm NUMBER(8,2) := vSal * 0.10;
vNote VARCHAR2(200) := Eligible for commission;
BEGIN
DECLARE
vSal NUMBER(8,2) := 90000;
vComm NUMBER (4) := 0;
vAnnualComp NUMBER(8,2) := vSal + vComm;
BEGIN
vNote := Manager not||vNote;
OUTER_BLK.vComm := vSal * 0.20;
END;
vNote := Salesman||vNote;
END;
21
SQL Star International Ltd.
income
is
/
Here, the keywords are aligned in the same line but at different levels. This
improves readability of the code.
22
SQL Star International Ltd.
Summary
In this chapter, you have learnt that:
PL/SQL bridges gap between SQL and a procedural language.
Two types of PL/SQL blocks are:
1. Anonymous or unnamed blocks which are not stored in database and
compiled each time they are executed.
2. Named Blocks which are compiled only once and stored in the database.
The three sections of a PL/SQL Block are:
1. Declarative Section, where all the variables used in the program are
declared. This is optional. Keyword: DECLARE
2. Executable Section where actual logic of the program lies. Keyword: BEGIN
3. Exception Section where all the errors are handled. This section is optional.
Keyword: EXCEPTION
4. END to end the PL/SQL program.
PL/SQL variables are declared and are accessible only within that PL/SQL block.
Non- PL/SQL variables are declared outside the PL/SQL block and are available
throughout the session.
Two new datatypes FLOAT and DOUBLE introduced in this release help users in
computing complex calculations.
Nesting of blocks is allowed to have good control on the scope of the variables.
Nested Blocks can also have exception sections.
23
SQL Star International Ltd.
Lab Exercises
1.
d) DECLARE
on BOOLEAN := 5;
2.
Create an anonymous block to display the phrase Welcome to the world of
PL/SQL.
3.
Create a block that declares two variables, one of VARCHAR2 type and the
other of NUMBER type. Assign the following values to the variables and display the
output on the screen.
4.
Write a PL/SQL code to display the ID, Last Name, job ID and salary of an
employee?
24
SQL Star International Ltd.
5.
6.
BINARY_INTEGER
b)
BINARY_FLOAT
c)
BINARY_DOUBLE
25
SQL Star International Ltd.
Chapter 2
DMLs in PL/SQL
SELECT Statements in PL/SQL
DML Statements in PL/SQL
26
SQL Star International Ltd.
Objectives
At the end of this chapter, you will be able to:
Use SELECT statements within PL/SQL blocks
Perform Data Manipulations within PL/SQL blocks
27
SQL Star International Ltd.
28
SQL Star International Ltd.
The following example will illustrate the use of SELECT statements within PL/SQL
blocks:
SET SERVEROUT ON
DECLARE
vBkname Book.cBookName%TYPE;
Output
vAuthor Book.cAuthorName%TYPE;
Variables
BEGIN
SELECT cBookName, cAuthorName
INTO vBkname, vAuthor
FROM Book
WHERE cBookID = HUM020000323;
DBMS_OUTPUT.PUT_LINE(vBkname|| written by: ||vAuthor);
END;
/
In the above program, the block retrieves the book name and author name for the
specified book.
In the following code SQL functions are used in the SELECT statement within the
block:
DECLARE
vTotcopies NUMBER(4); Output variable
vBrID Book.cBranchID%TYPE:=02CHSNJ;
BEGIN
SELECT SUM(nNoOfCopies)
INTO vTotcopies
FROM Book
WHERE cBranchID = vBrID;
29
SQL Star International Ltd.
DBMS_OUTPUT.PUT_LINE(The
total
stock
of
books
stored
at
the
Chester NJ Library is: ||vTotcopies);
END;
/
30
SQL Star International Ltd.
While embedding SELECT statements within PL/SQL blocks you must remember the
following points:
The number of output variables in the INTO clause must be the same as the
number of columns selected in the SELECT clause.
Make sure that the output variables in the INTO clause are mapped correctly
to the selected columns, and that their datatypes are also compatible.
To ensure that the datatypes of variables match that of the columns selected
in the SELECT clause, use the %TYPE attribute.
You can use group functions in the SELECT statement but not in the PL/SQL
statements.
Do not give names to variables that are identical to column names. This could
create ambiguity in the WHERE clause.
31
SQL Star International Ltd.
32
SQL Star International Ltd.
This block does not have a declarative section, as you have not declared any
variables that are to be used in the INSERT statement. The VALUES clause requires:
The actual values Ann and Judd to be entered into the cFirstName and
cLastName columns respectively
The SYSDATE function to enter the current date into the dMembershipDt
column.
You can also insert the above details in the following manner:
DECLARE
cFirstName Member.cFirstName%TYPE DEFAULT Ann;
BEGIN
INSERT INTO
Member(cMemberID,cFirstName,cLastName,vAddress,
cArea,cZipcode,cPhone,nAge,dMembershipDt,
cGrade,cMaritalStatus,cBranchID)
VALUES (CAJ040501,cFirstName,Judd,
26A,Due Drops Apts,Blue Mountain Villa,
Elizabeth,78099',NULL,22,SYSDATE,C,N,04RANNJ);
END;
/
The block declares an input variable cFirstName of Member.cFirstName%TYPE and
initializes it with a value Ann using the DEFAULT keyword instead of the assignment
operator. This variable is then used in the VALUES clause to populate cFirstName
column with the value Ann.
Therefore, in the INSERT statement you can:
33
SQL Star International Ltd.
You can make changes to data or remove data stored in the database by embedding
UPDATE and DELETE statements within PL/SQL blocks.
Update the number of copies of the book On The Street Where You Live in
the Book table
Remove details pertaining to one Mr. Derian Bates from the Member table
DECLARE
vCopiesIncrease Book.nNoOfCopies%TYPE:=3;
vMemID Member.cMemberID%TYPE: = CDB028504;
BEGIN
UPDATE Book
SET nNoOfCopies = nNoOfCopies + vCopiesIncrease
WHERE cBookName = On The Street Where You Live;
DELETE FROM Member
WHERE cMemberID = vMemID;
COMMIT;
END;
/
It is important to remember that there could be ambiguity in the SET clause if the
declared variable has the same name as that of the column whose value is to be
updated. This is because the name on the left of the assignment operator in the SET
clause always corresponds to a database column, but on the right side, it could
correspond either to a database column or a PL/SQL variable.
34
SQL Star International Ltd.
Use the MERGE statement to update or insert rows in a table using data from
another table. Rows are inserted or updated based on a specified equijoin condition.
For example,
DECLARE
vMemID Member.cMemberID%TYPE:= CDB028504;
BEGIN
MERGE INTO MemberTabCopy mc
USING Member m
ON (m.cMemberID=vMemID)
WHEN MATCHED THEN
UPDATE SET
mc.cFirstName = m.cFirstName,
mc.cLastName = m.cLastName,
mc.vAddress = m.vAddress,
mc.cArea = m.cArea,
mc.cZipcode = m.cZipcode,
mc.cPhone = m.cPhone,
35
SQL Star International Ltd.
mc.nAge = m.nAge,
mc.dMembershipDt = m.dMembershipDt,
mc.cGrade = m.cGrade
36
SQL Star International Ltd.
Summary
In this chapter, you have learnt that:
Embedded SELECT statement has a INTO clause in it. One or more variable
follows after the INTO clause.
Variable names should not collide with the names of the database columns.
Data fetched from the database are stored in these variables.
WHERE clause restricts the number of rows to be fetched into these scalar
variables.
By doing so, one can reduce the network traffic, resulting in the performance
improvement.
37
SQL Star International Ltd.
Lab Exercises
1.
Create a PL/SQL block that retrieves the sum of salary of department 80 and
stores it in an iSQL*Plus variable. Print the value to the screen.
2.
38
SQL Star International Ltd.
Chapter 3
Control Structures
Conditional Constructs
Case Constructs
Loop Constructs
GOTO Statement
39
SQL Star International Ltd.
Objectives
At the end of this chapter, you will be able to:
40
SQL Star International Ltd.
Control Structures
Oracle PL/SQL provides a range of constructs that allow you to control the flow of
process and there by produce well-structured programs. The selection structure tests
a condition and then executes one sequence of statements instead of another, based
on whether the condition is true or false. A condition is any variable or expression
that returns a Boolean value (TRUE or FALSE). The iteration structure executes a
sequence of statements repeatedly as long as a condition holds true. The sequence
structure simply executes a sequence of statements in the order in which they occur.
Hence, Control structures are the backbones of a program, where logic of the
transactions are specified.
Control Structures are broadly divided into
Conditional constructs
IF Statements
CASE Statement and CASE Expression
Loop constructs
Basic Loop
WHILE Loop
FOR Loop
CONDITIONAL CONSTRUCTS USING IF Statements
When you need to perform actions based on conditions, you use the IF statements.
The structure of the IF statement is the same as those used in other procedural
programming languages. It has three variations. They are:
IF - THEN - END IF
IF - THEN - ELSE - END IF
IF - THEN - ELSIF - END IF
Their syntaxes are shown below.
IF condition THEN
statements;
END IF;
IF condition THEN
statements;
[ELSE
statements;]
END IF;
IF condition THEN
statements;
[ELSIF condition THEN
statements;]
[ELSE
statements;]
END IF;
41
SQL Star International Ltd.
Where,
condition is an expression, which when executed will return a value of TRUE or
FALSE or NULL. If the result of the expression is TRUE then the set of statements,
following the THEN clause is performed else the statements following the ELSE or
ELSIF clauses are performed.
THEN is the clause that connects the value returned by the condition to the
statements that are to be executed
statements include the SQL or PL/SQL statements that would perform the required
actions. They may include further IF statements.
The ELSIF keyword is associated with another set of SQL or PL/SQL statements that
should be executed in case the condition does not return the desired value.
The ELSE keyword is also associated with a set of SQL or PL/SQL statements that will
be executed, if none of the statements higher up in the construct has been executed,
as the condition associated with each was not satisfied.
Let us now look into a few examples of the various IF statements, to have a better
understanding of its functioning.
Example of a simple IF statement:
The library does not permit a person to be its member if his/her age happens to be
below five. This could be checked using the following IF-THEN-END IF statement:
DECLARE
Age Member.nAge%TYPE:= &age;
BEGIN
IF Age<5 THEN
DBMS_OUTPUT.PUT_LINE
(Membership
age);
END IF;
END;
/
denied
Invalid
If you execute the above code entering the value for age as 4, you get the following
output:
Membership denied Invalid age
PL/SQL procedure successfully completed.
In the code, PL/SQL displays the message only if the condition is TRUE. If the
condition is FALSE or NULL, PL/SQL just ignores them. But if you want some action
to be performed in case of FALSE or NULL result, use the ELSE clause.
42
SQL Star International Ltd.
43
SQL Star International Ltd.
Fee NUMBER;
BEGIN
IF Age < 5 THEN
DBMS_OUTPUT.PUT_LINE (Membership
denied: Age below permissible
membership age);
ELSE
IF Age BETWEEN 5 AND 13 THEN
Fee := 10;
ELSE
IF Age BETWEEN 14 AND 20 THEN
Fee := 15;
ELSE
IF Age BETWEEN 21 AND 50 THEN
Fee := 20;
ELSE
IF Age > 50 THEN
Fee := 5;
END IF;
END IF;
END IF;
END IF;
END IF;
DBMS_OUTPUT.PUT_LINE (Fee for Member is:|| Fee);
END;
/
If you execute the above code, you can see the following output:
Enter value for age: 44
old
2: Age NUMBER :
=
new
2: Age NUMBER :
=
Fee for Member is:20
&age ;
44 ;
44
SQL Star International Ltd.
DECLARE
Age NUMBER := &age ;
Fee Number;
BEGIN
IF Age < 5 THEN
DBMS_OUTPUT.PUT_LINE (Membership denied: Age below
permissible membership age);
ELSIF Age BETWEEN 5 AND 13 THEN
Fee := 10;
ELSIF Age BETWEEN 14 AND 20 THEN
Fee := 15;
ELSIF Age BETWEEN 21 AND 50 THEN
Fee := 20;
ELSIF Age > 50 THEN
Fee := 5;
END IF;
DBMS_OUTPUT.PUT_LINE (Fee for Member is:|| Fee);
END;
/
On executing the above code, you get the following result:
Enter value for age: 55
old
2: Age NUMBER : = &age ;
new
2: Age NUMBER : = 55 ;
Fee for Member is:5
PL/SQL procedure successfully completed.
After having used all the variations of the IF constructs, there are certain guidelines
to remember while writing them in a program. They are:
END IF is not one word but two words and ELSIF is one word.
Do not use a semicolon (;) on the lines with the IFTHEN, ELSIF, and ELSE
keywords. END IF must be followed by a semicolon (;).
You could indent the SQL and PL/SQL statements to be executed to increase
readability of the code.
45
SQL Star International Ltd.
CASE expressions: The expressions select a result and return it. CASE
expressions can be assigned to variables, can be part of SQL statements and
can be used in logical expressions. The syntax begins with CASE and ends
with END.
CASE statements: They are independent statements just like other PL/SQL
statements such as IFTHENELSE statements. The syntax begins with CASE
and ends with END CASE.
CASE Expressions
CASE expressions select a result and return it. To understand better, look at the
following syntax of a CASE expression:
CASE selector
WHEN expr1 THEN result1
WHEN expr2 THEN result2
46
SQL Star International Ltd.
47
SQL Star International Ltd.
Stands
for:
In the code, you will notice that the CASE statement ends with an END CASE
statement, and that every executable statement following the WHEN clause is
terminated with a
semicolon. The above code accepts the value of vGrade and based on it assigns
different values to vGradeWiseAge variable.
A searched CASE statement example:
DECLARE
vAge NUMBER(3):= &age;
vAgeWiseGrade CHAR(2);
BEGIN
CASE
WHEN vAge<5 THEN vAgeWiseGrade:=Membership not permitted;
WHEN vAge BETWEEN 5 AND 13 THEN vAgeWiseGrade:=A;
WHEN vAge BETWEEN 14 AND 20 THEN vAgeWiseGrade:=B;
WHEN vAge BETWEEN 21 AND 50 THEN vAgeWiseGrade:=C;
WHEN vAge BETWEEN 50 AND 100 THEN vAgeWiseGrade:=D;
END CASE;
DBMS_OUTPUT.PUT_LINE
(For
Age
:
||vAge||
The
corresponding grade is: ||vAgeWiseGrade);
48
SQL Star International Ltd.
END;
/
In the code, you will observe that the searched CASE statement is similar to the
searched CASE expression except that the former cannot be assigned to variables.
49
SQL Star International Ltd.
value is after SYSDATE, then the vDate value is returned to vReturnDt variable. If
vDate variable value is same as SYSDATE, then a NULL value is returned.
The COALESCE expression behaves like the NVL function, but it can take a list of
values. Its semantics can be written as follows:
LOOP Constructs
You might require performing a set of steps continuously till the transaction or task
to be performed is complete.
Using loops will enable you to perform such kind of execution. The execution of the
statements in a loop also depends on the result of a condition being true or false. As
long as the condition is true, the statements in the loop will be executed. The
condition to be satisfied is the starting point of the loop. Only when the condition is
satisfied, the control of the block will enter the loop and will stay in the loop till the
condition is reversed. An important point to remember is that the loop should
provide for a condition to be checked for exiting from the loop.
The different looping constructs include:
BASIC loop
FOR loop
WHILE loop
BASIC LOOP
This is the simplest form of a loop. The statements are enclosed between the
keywords LOOP and END LOOP.
50
SQL Star International Ltd.
When the flow of execution encounters the END LOOP keyword, the control goes
back to the corresponding LOOP statement above it. Hence, the statements in a
basic loop will be executed at least once.
The loop should contain an EXIT statement. This statement helps to end the loop. If
it is not specified, then the loop will be endless. An EXIT statement can also be
specified as an action within an IF statement. The EXIT statement must be placed
within the loop.
Result:
51
SQL Star International Ltd.
0
2
4
6
8
10
12
14
16
18
PL/SQL procedure successfully completed.
The EXIT-WHEN statement replaces a simple IF statement. For example, compare
the following statements.
Where,
counter is an integer, which is implicitly declared and whose value increases (or
decreases when used with the REVERSE keyword) by 1 until its value reaches either
of the limits specified.
REVERSE enables the counter to decrement in intervals of 1.
lower_bound is the lower limit for the range of the counter value.
upper_bound is the upper limit for the range of the counter value.
52
SQL Star International Ltd.
The values for these limits can be strings, variables or expressions or numeric
values. When the lower value is increased to a values higher or greater than the
specified upper limit then the loop will not be executed.
Certain points to remember when using a FOR loop is that:
The counter can be referenced only inside the loop.
When referring to the counter use an expression to refer to its existing value.
Cannot assign a value to the counter.
Use a FOR loop instead of the basic loop to display the first ten even numbers.
DECLARE
EvenNo NUMBER(2): = 0;
BEGIN
FOR I IN 1..10
LOOP
DBMS_OUTPUT.PUT_LINE(EvenNo);
EvenNo: = EvenNo +2;
END LOOP;
END;
/
This code generates the following output:
Result:
0
2
4
6
8
...
...
PL/SQL procedure successfully completed.
WHILE LOOP
In this loop construct, the statements are executed based on a condition at the
beginning of the loop. The condition controls the end of the loop as well. In other
words, the statements in the loop will be executed as long as the condition specified
is true.
The condition is checked at the beginning of the LOOP and if the condition is FALSE,
the loop is terminated and none of the statements are executed. The syntax is:
53
SQL Star International Ltd.
GOTO Statement
The GOTO statement branches to a label unconditionally. The label must be unique
within its scope and must precede an executable statement or a PL/SQL block. When
executed, the GOTO statement transfers control to the labeled statement or block. In
the following example, you move to an executable statement further down in a
sequence of statements:
BEGIN
...
GOTO insert_row;
...
<<insert_row>>
INSERT INTO emp VALUES ...
END;
/
In the next example, you move to a PL/SQL block further up in a sequence of
statements:
DECLARE
x NUMBER := 0;
BEGIN
<<increment_x>>
BEGIN
x := x + 1;
54
SQL Star International Ltd.
END;
IF x < 10 THEN
GOTO increment_x;
END IF;
END;
/
The label <<end_loop>> in the following example is not allowed because it does not
precede an executable statement:
DECLARE
done BOOLEAN;
BEGIN
FOR i IN 1..50
LOOP
IF done THEN
GOTO end_loop;
END IF;
<<end_loop>> --not allowed
END LOOP; --not an executable statement
END;
/
To correct the previous example, add the NULL statement::
FOR i IN 1..50
LOOP
IF done THEN
...
GOTO end_loop;
END IF;
...
<<end_loop>>
NULL; -- an executable statement
END LOOP;
55
SQL Star International Ltd.
Nested Loops
You can write a loop within another loop. The inner loop is called a nested loop as it
is nested within a loop, which is called the parent loop. You can nest FOR, WHILE
and BASIC loops within one another. If the nested loop is terminated, the parent
loop does not get terminated unless there is an exception raised in the nested loop.
Loops can be labeled, to identify them. These are like identifiers and are placed
before the word LOOP within label delimiter (<<label>>). You could also place the
label after the END LOOP keyword.
Syntax for a nested loop is:
. . .
BEGIN
<<outerloop>>
LOOP
counter_variable:= counter_variable + 1;
EXIT WHEN nCount>20;
<<innerloop>>
LOOP
. . .
EXIT outerloop WHEN condition;
. . .
EXIT WHEN condition;
. . .
END LOOP innerloop;
. . .
END LOOP outerloop;
END;
Where,
BEGIN is the start of the block.
LOOP on the second line marks the beginning of the first loop, labeled outerloop.
counter_variable is the counter that is used to check the number of times the loop is
being executed and is defined earlier.
EXIT WHEN is the condition to exit the outer loop.
The next LOOP marks the beginning of the nested or inner loop labeled innerloop.
EXIT outerloop will exit both the loops based on the condition.
EXIT WHEN on the next line will exit only the nested loop if the condition is satisfied.
The control goes back to the outerloop.
END LOOP innerloop ends the inner loop and execution of the outer loop resumes if
there are any statements to be executed.
56
SQL Star International Ltd.
inner_loop;
inner_done:=No;
DBMS_OUTPUT.PUT_LINE(*****Printing Whole Number from
outer
loop***** ||counter);
57
SQL Star International Ltd.
Summary
In this chapter, you have learnt that:
Statements such as IF and CASE give a directive approach to a program logic.
Using IF statement, multiple nested conditions can be solved. Where as CASE
statement improves the readability of a program.
The Iteration of a program can be controlled using
1. Basic LOOP that runs at least once
2. WHILE LOOP that runs till the specified condition is true
3. FOR loop that executes till the specified Iteration in the loop.
These loops can be nested within one another.
Jumping from one point to other in a program can be done with GOTO
Statement using the LABEL Option.
58
SQL Star International Ltd.
Lab Exercises
1.
59
SQL Star International Ltd.
Chapter 4
Composite Data-types
Types of Composite Data-types
INDEX BY Tables
INDEX BY Table of Records
Varying Arrays
Nested Tables
Ref Cursor
60
SQL Star International Ltd.
Objectives
At the end of this chapter, you will be able to:
61
SQL Star International Ltd.
User-defined records
%ROWTYPE records
62
SQL Star International Ltd.
Collections
INDEX BY Table
Nested Table
VARRAY
Reference (REF)
PL/SQL Records
A record is defined as a collection of different but related data items stored in fields.
For example, the New Jersey Central Library has different kinds of data regarding its
members, such as their ID, name, address, membership date, age, etc. Each piece
of information or data item is dissimilar in type, but it is all logically related to
members. Therefore, a member record is made up of a collection of these logically
related data items.
In PL/SQL these data items are stored in fields and a PL/SQL record is made up of a
collection of such fields.
A PL/SQL record is similar in concept and structure to a row in a database table. Just
as a row in the Member table contains information about members stored in
columns, similarly a record contains information of members stored in fields.
Therefore, a record is a composite data structure, as it comprises more than one
element or field. The record as a whole does not have any value of its own. Instead,
each individual field in a record has a value. The record enables you to store and
access these values as a group rather than accessing them from the database.
To create a record, you should first create a record type and then declare records of
that type. There are two types of PL/SQL records:
User-defined records
%ROWTYPE records
User-defined Records
A user-defined record is a composite datatype that allows you to declare fields of
your own choice. You can define their datatypes based on database columns using
%TYPE attribute. The syntax is:
63
SQL Star International Ltd.
}[NOT
Before you start using PL/SQL records, you need to understand how to create them.
64
SQL Star International Ltd.
recBook rectpBk;
A record type rectpBk has been created which is made up of three fields
namely BookID, BkName and Author. The %TYPE attribute is used to
specify the datatypes of BkName and Author.
If you have not used the NOT NULL or the DEFAULT keyword to initialize fields, you
must reference or initialize them explicitly.
Initializing Records
You can reference or initialize record fields using the following syntax:
<record_name>.<field_name>
In the syntax the dot notation is used between the record name and the field name.
In the above code snippet, you would reference the Bkname field as follows,
recBook.Bkname
and initialize it with a value,
RecBook.Bkname:= Illusions;
However, you cannot assign a list of values to a record by using an assignment
operator. The following syntax is illegal:
<record_name> := (value1, value2, value3, ...);
For instance, you cannot assign the values of book id, book name and author name
all together to the record recBook in the following manner:
recBook:=(0109918818122,The Jewel In The Crown, Paul
Scott);
Also, you cannot test records for equality or inequality. For instance, the following IF
statement is illegal:
IF record_name1 = record_name2 THEN
END IF;
You now know the syntax for assigning values to fields, but there are different ways
to do it.
Way of assigning values to Records
There are two ways to assign values to fields:
Instead of assigning values to fields individually, you can assign values to all
the fields at once. This can be done using a SELECT statement in the code
where you had declared recBook record.
65
SQL Star International Ltd.
DECLARE
BkID Book.cBookID%TYPE:=&Bkid;
TYPE rectpBk IS RECORD
(Bookid CHAR(13),
Bkname Book.cBookName%TYPE,
Author Book.cAuthorName%TYPE);
recBook rectpBk;
BEGIN
SELECT cBookID,cBookName,cAuthorName
INTO recBook
FROM Book
WHERE cBookID=BkID;
DBMS_OUTPUT.PUT_LINE(recBook.Bookid|| || recBook.Bkname||
||recBook.Author);
END;
/
In the code, the fields of recBook have been populated with values retrieved from
the Book table by the SELECT statement. But you must ensure that the column
names in the SELECT statement appear in the same order as the fields in the record.
Also, their datatypes should be compatible with one another.
On executing this code, the values are retrieved from the record rather than from
the database table Book. This reduces your network traffic.
Enter value for bkid: FIC011111111
FIC011111111
The second way is to assign one record to another. The following code
illustrates this:
DECLARE
TYPE rectpBk IS RECORD
(Bkid CHAR (15),
66
SQL Star International Ltd.
This code snippet illustrates the declaration of nested records, i.e. a record defined
as an element or a field of another record. For instance, Bk is a nested record as it is
defined as a field (which is of rectpBk type) of recCtg, recBks and recBrn records.
You can assign the values of a nested record to another if they are of the same
datatype. For instance, recCtg and recBks are both of the same datatype,
rectpBkCtg. The nested record Bk has been assigned values:
recCtg.Bk.Bkid:=0117955574699;
recCtg.Bk.Bkname:=Very Good Jeeves;
You can now assign them to recBks record in the following manner:
67
SQL Star International Ltd.
recBks.Bk:=recCtg.Bk;
Such assignments are also permissible if the records that contain the nested records
are of different datatype. For instance, recBrn record of rectpBrn record type,
which contains the nested record Bk. Yet, the values of Bk initialized by recCtg
record can be assigned to recBrn.
recBrn.Bk:=recCtg.Bk;
The problem of not knowing the column details of a database table and unexpected
changes taking place in the number and datatype of columns can be overcome using
the %ROWTYPE attribute. The % ROWTYPE attribute enables you to create
%ROWTYPE records, which are similar to rows in a database table.
%ROWTYPE Records
Sometimes you may want to create a record whose record type is same as a row of a
database table (or a view). Such records are known as %ROWTYPE records. Just as
you can create a record based on a user-defined record type, similarly, you can
create a %ROWTYPE record using the %ROWTYPE attribute. The %ROWTYPE
attribute enables you to declare a record type that represents a row in a table or a
view.
Creating %ROWTYPE Records
Prefixing % ROWTYPE with the table name creates %ROWTYPE records. The syntax
for declaring a %ROWTYPE record is:
<record_name>
reference%ROWTYPE;
Where,
record_name is the name you assign for a record.
reference specifies the name of a table, view or cursor on which you want the record
to be based.
68
SQL Star International Ltd.
69
SQL Star International Ltd.
CBW109702 CLS020005771
The code creates a record recTran of %ROWTYPE that represents an entire row of
Transaction table. The column values in the Transaction table are assigned to the
fields in the recTran record using the SELECT statement. The same code can be
written using cursors. Try it yourself.
As mentioned earlier in the chapter, records are mainly created to reduce network
traffic and save server resources. But how is the New Jersey Central Library
achieving this? The following scenario will make it clear how the library is making use
of %ROWTYPE records.
The library maintains a table called NonMembers, that stores the details of all
members whose membership has been terminated. As the details are similar to the
Member table, you can use this to insert values into NonMembers table, by writing
the following code:
DECLARE
vMemID Member.cMemberID%TYPE:=&memid;
recMember Member%ROWTYPE;
BEGIN
SELECT *
INTO recMember
FROM Member
WHERE cMemberID=vMemID;
INSERT INTO NonMember (nSerialNo,cMemberID,cFirstName,
cLastName,cPhone,dMembershipDt,cBranchID)
VALUES(sqSerialNo.nextval,recMember.cMemberID,recMember.cFirstName,
recMember.cLastName,recMember.cPhone,recMember.dMembershipDt,
recMember.cBranchID);
COMMIT;
END;
/
In the code, the programmer declares a recMember record to store the details of a
member, as it is stored in the Member table, by using the %ROWTYPE attribute.
Values are assigned to the fields of recMember record by the SELECT statement
that retrieves details of the member whose ID is supplied.
70
SQL Star International Ltd.
The assigned values are then inserted into NonMember table by using the dot
notation syntax of recMember.cFirstName, recMember.cLastName, and so on.
Whenever the membership of a member has to be terminated, his ID is supplied to
execute the above
PL/SQL block. This block will select his details from the Member table and store it in
the record created. His details are then inserted into the NonMember table from the
record and not from the database table. This code serves two purposes. Firstly, it
automates entries into the NonMember table. Secondly, it reduces the servers
network traffic and memory usage.
71
SQL Star International Ltd.
FROM Member
WHERE cMemberID=vMemID;
UPDATE
MemberCopyTab
SET ROW=recMember
WHERE Memberid=vMemID;
COMMIT;
END;
/
The example shows how to update a row using a record. The keyword ROW is used
to represent the entire row. The code shown in the example updates the
MemberCopyTab table with MemID detail of the Member table.
By now, as you should be familiar with PL/SQL records, let us move on to the next
type of composite variable INDEX BY tables.
INDEX BY Tables
Like PL/SQL records, INDEX BY tables are also composite variables and are created
based on a composite datatype TABLE. INDEX BY tables are objects of TABLE type,
which are designed as database tables but are not the same as database tables.
For example, an INDEX BY table of book names is designed as a database table with
two columns, one that stores the primary key and the other that stores the character
data, book names. But unlike a database table you cannot manipulate INDEX BY
table by using SQL statements. However, the primary key column enables you an
array-like access to rows. Consider the primary key values as index and rows of the
INDEX BY table as elements of a one-dimensional array.
A one-dimensional array is an ordered list of elements of the same type. An array
element is accessed by its index. An index number determines the position of an
element in an ordered list. All array indexes start at zero. For instance, an array
contains 12 elements of character datatype representing names of months. Each
element is assigned an index with which they are to be accessed.
To access the fourth month of a year, you should access index number 3. However,
72
SQL Star International Ltd.
Arrays have their lower and upper limit defined. For instance, the array
created above has a limit of 12 elements. It cannot accommodate a single
element exceeding its limit. However, the size of an INDEX BY table is not
fixed and can be increased dynamically.
Secondly, arrays necessarily must have consecutive index numbers. But this
is not a constraint in INDEX BY tables and this characteristic is known as
sparsity. For instance, to index book names in an INDEX BY table, you can
use book IDs that are not consecutive.
73
SQL Star International Ltd.
The code snippet creates tbtypMemID TABLE type that will have the column type of
cMemberID.
After declaring a TABLE type, you need to create an INDEX BY table of the TABLE
type. You can create an INDEX BY table by using the following syntax:
identifier
<type_name>;
Where,
identifier is the name assigned to the INDEX BY table declared of type_name.
The following code snippet illustrates the declaration of an INDEX BY table of
tbtypBookName TABLE type:
DECLARE
TYPE tbtypBookName IS TABLE OF Book.cBookName%TYPE
INDEX BY PLS_INTEGER;
tabBkName tbtypBookName;
74
SQL Star International Ltd.
By defining NOT NULL constraint on the tbtypBookName TABLE type, null values
are prevented from being entered into tabBkName INDEX BY table.
The INDEX BY table tabBkName has the following structure:
tabBkName INDEX BY table has one column and a primary key neither of which are
named. The column is of scalar datatype and the primary key is of PLS_INTEGER
type. The primary key plays an important role when it comes to referencing an
INDEX BY table.
The size of tabBkName is unconstrained or unbounded. Its number of rows can
increase dynamically as more books are added into the already existing list of books
in the Book table.
You cannot initialize tabBkName INDEX BY table in its declaration. By doing so, you
are limiting the number of books. In a library, there would always be additions to the
Book table. By defining a limit, your INDEX BY table would not reflect changes made
to its underlying database column.
75
SQL Star International Ltd.
DECLARE
vBrId Book.cBranchID%TYPE:=&BrId;
TYPE tbtypBookName IS TABLE OF Book.cBookName%TYPE
INDEX BY BINARY_INTEGER;
tabBkName tbtypBookName;
BEGIN
tabBkName(1):=Jewel In The Crown;
UPDATE Book
SET nNoOfCopies = nNoOfCopies + 3
WHERE cBookName = tabBkName(1)
AND cBranchID=vBrId;
END;
/
On executing the code, you get the following result:
Enter value for brid: 02CHSNJ
PL/SQL procedure successfully completed.
INDEX BY tables are mainly used to move collections of data into and out of
database tables to reduce network traffic and save server resources. For example,
the New Jersey Central Library may want to temporarily store the details of nonmembers. The programmer will write the following code to retrieve the member IDs
from the NonMember table and store them in a INDEX BY table in the following
way:
DECLARE
TYPE tbtypMemID IS TABLE OF NonMember.cMemberID%TYPE
INDEX BY BINARY_INTEGER;
tabMemID tbtypMemID;
nCount NUMBER (2);
BEGIN
76
SQL Star International Ltd.
77
SQL Star International Ltd.
This list proves to be helpful when the librarian wishes to compare it with the
existing list of members in the Member table. By doing so, he will be able to identify
those who are no longer members of the library.
So far, the INDEX BY tables you created were based on TABLE types consisting of
scalar datatypes. You can also base them on record types. Such INDEX BY tables are
known as INDEX BY table of records.
DECLARE
CURSOR curBk IS
SELECT * FROM Book
WHERE dPublishedYr between 01-JAN-1950' AND 01-JAN-1975;
TYPE tbtypBk IS TABLE OF Book%ROWTYPE
INDEX BY BINARY_INTEGER;
78
SQL Star International Ltd.
tabBk tbtypBk;
i NUMBER := 0;
BEGIN
OPEN curBk;
LOOP
i := i+1;
FETCH curBk INTO tabBk(i);
EXIT WHEN curBk%NOTFOUND;
DBMS_OUTPUT.PUT_LINE
tabBk(i).cBookName
(The
||written
book||
||
by||
||
in
the
year|| ||tabBk(i).dPublishedYr);
END LOOP;
CLOSE curBk;
End;
/
On executing this code, you will get the following output:
The book Jewel In The Crown written by Paul Scott was published in the
year 25-NOV-66
PL/SQL procedure successfully completed.
To further enhance the functionality of INDEX BY tables, there are some built-in
procedures or functions that operate on them. These built-ins are covered next.
<indexbytable_name>.<attribute_name>[(parameters)]
79
SQL Star International Ltd.
EXISTS
COUNT
FIRST
LAST
PRIOR
NEXT
TRIM
DELETE
Of these methods, EXISTS, PRIOR, NEXT, EXTEND and DELETE take parameters.
These parameters must be expressions that produce a BINARY_INTEGER value or
values that can implicitly be converted in to a value of that type.
EXISTS Method
EXISTS (n) returns TRUE if the nth element exists in an INDEX BY table. This
attribute is used to avoid the error that would be generated while referencing
nonexistent elements.
In the following example, PL/SQL executes the UPDATE statement only if the
element tabBkName (1) exists:
DECLARE
vBrId Book.cBranchID%TYPE:= &BrId;
TYPE tbtypBookName IS TABLE OF Book.cBookName%TYPE
INDEX BY BINARY_INTEGER;
tabBkName tbtypBookName;
BEGIN
tabBkName(1):=The Crown;
IF tabBkName.EXISTS(1) THEN
UPDATE Book
SET nNoOfCopies = nNoOfCopies + 3
WHERE cBookName = tabBkName(1)
AND cBranchID = vBrId;
END IF;
COMMIT;
80
SQL Star International Ltd.
END;
/
COUNT Method
The COUNT method returns the number of elements contained within an INDEX BY
table. You can use COUNT wherever integer expression is allowed. For example, the
following code creates a tabMemID INDEX BY table. Using COUNT you can ascertain
the total number of books contained in it.
DECLARE
TYPE tbtypMemID IS TABLE OF NonMember.cMemberID%TYPE
INDEX BY BINARY_INTEGER;
tabMemID tbtypMemID;
nCount NUMBER (2);
BEGIN
SELECT COUNT(*)
INTO nCount
FROM NonMember;
FOR i IN 1..nCount
LOOP
SELECT cMemberID
INTO tabMemID(i)
FROM NonMember
WHERE nSerialNo = i;
END LOOP;
FOR i IN 1..nCount
LOOP
DBMS_OUTPUT.PUT_LINE (tabMemID (i));
END LOOP;
DBMS_OUTPUT.PUT_LINE (The total number of books contained
in tabBkName is:||tabMemID.COUNT);
END;
81
SQL Star International Ltd.
The COUNT method is useful because the future size of an INDEX BY table is
unconstrained. For example, running the above code displays the total number of
elements contained in tabMemID.
CAC033105
DSP023205
CLK029204
DMA029204
DAC078801
CKB109305
The total number of books contained in tabMemID is:6
PL/SQL procedure successfully completed.
But if you run the same code after few days, you would find an increase in the
number of elements because the INDEX BY table is unconstrained and reflects any
change in the database table.
(The
book
contained
(The
book
contained
in
the
first
index
is:
last
index
is:
||tabMemID.FIRST);
DBMS_OUTPUT.PUT_LINE
in
the
||tabMemID.LAST);
END;
82
SQL Star International Ltd.
--NULL is assigned to n
You can use PRIOR and NEXT in tabMemID to display the member IDs that appear
prior to the last one and next to the first one as follows:
DBMS_OUTPUT.PUT_LINE (tabMemID.PRIOR (tabMemID.LAST));
DBMS_OUTPUT.PUT_LINE (tabMemID.NEXT (tabMemID.FIRST));
TRIM Method
The TRIM method removes one element from the end of an INDEX BY table. TRIM
(n) removes n elements from the end of an INDEX BY table. For example, you can
trim one element from the end of tabBkName INDEX BY table in the following
manner:
tabBkName.TRIM;
DELETE Method
The DELETE method has three forms. They are:
For example, the following snippet deletes the 20th element from tabBk INDEX BY
table.
tabBk.DELETE (20);
You can confirm this by issuing the following loop in the executable section of the
block:
FOR i IN 1..tabBk.COUNT
LOOP
DBMS_OUTPUT.PUT_LINE (tabBk (i). cBookName);
END LOOP;
END;
This statement uses the COUNT method to specify the upper limit of the FOR loop.
The COUNT attribute will result in one element less than the total number of
elements and therefore the 20th element is not displayed.
83
SQL Star International Ltd.
Varying Arrays
Collections also include Varrays and Nested Tables. This section covers varrays.
If a record has more than one value for any of its attributes, then you would have to
enter the record into the table, as many times as there are values for the attribute.
This violates the primary key constraint and causes redundancy as the same record
gets stored multiple times. This problem can be taken care of by using varying
arrays. They are used to store multiple values of attributes of a record in the same
row.
84
SQL Star International Ltd.
For instance, in a library people borrow more than one book at a time. The library
has a rule that they will issue three books at a time to a member. Hence instead of
recording three transactions with different transaction IDs for the same customer,
the database should allow the library clerk to enter only one record with details of
the three books that the member wants to borrow. The attribute of book ID could be
created as a varray as shown below:
CREATE TYPE vaBooksID AS VARRAY(3) of CHAR(13);
/
Type created.
This code creates a varray vaBooksID that can be used in the Transaction table to
record the IDs of the books borrowed by the member. To use this in the
Transaction table, you need to create the table specifying the varray in the CREATE
TABLE statement:
CREATE TABLE objTransaction
(cTranID CHAR(4) PRIMARY KEY,
cMemberID CHAR (9),
BookID vaBooksID,
dIssueDt DATE,
dReturnDt DATE,
dActualReturnDt DATE,
nFine NUMBER (4,2),
CONSTRAINT TranMemIDFK FOREIGN KEY (cMemberID) REFERENCES
Member (cMemberID) ON
DELETE CASCADE);
Table created.
When viewing the table structure using data dictionary views, the column BookID
will be displayed as a column of type vaBooksID.
To see the attributes of the varying array, query the view, USER_COLL_TYPE. It will
display the datatypes and their characteristics. You could also specify the particular
datatype about which you want information.
For instance the following SELECT statement:
SELECT COLL_TYPE, ELEM_TYPE_OWNER, ELEM_TYPE_NAME, UPPER_BOUND, LENGTH
FROM USER_COLL_TYPES
WHERE Type_Name = VABOOKSID;
The query displays the following information about the varray vaBooksID:
85
SQL Star International Ltd.
COLL_TYPE
--------
VARYING ARRAY
CHAR
LENGTH
-------13
86
SQL Star International Ltd.
vaBkID,
dIssueDt DATE,
dReturnDt DATE,
dActualReturnDt DATE,
nFine NUMBER (4,2),
CONSTRAINT TransMemIDFK FOREIGN KEY (cMemberID) REFERENCES
Member (cMemberID) ON
DELETE CASCADE);
/
Table created.
To add a row into the table, issue the following INSERT statement:
INSERT INTO objTrans VALUES
(I001, BTK119705, vaBkID( TypBook(FIC00400213),
TypBook( ROM002067)), 25/01/2001, 08/02/2001, NULL, NULL);
1 row created.
87
SQL Star International Ltd.
An important point to remember is that, you should not insert more values into the
varray than what is specified when creating it. This is known as the LIMIT of a varray
and is specified when creating the varray. You can query the USER_COLL_TYPES
view to confirm the maximum values that the varray can hold.
Selecting Data from a Varray
You retrieve data stored in a varray by using a loop within a cursor. This can be
understood better by the code given below. This code, when executed extracts the
book IDs borrowed by a member. (To be able to see the output set the
SERVEROUTPUT on)
DECLARE
CURSOR curBookIssue IS
SELECT * FROM objTransaction;
BEGIN
FOR BookRec in CurBookIssue
LOOP
DBMS_OUTPUT.PUT_LINE(Books borrowed by: ||
BookRec.cMemberID);
FOR I IN 1 .. BookRec.BookID.Count
LOOP
DBMS_OUTPUT.PUT_LINE (BookRec.BookID(I));
END LOOP;
END LOOP;
END;
/
This code displays the result as shown below.
Books borrowed by:BTK119705
FIC00400213
ROM0020670
88
SQL Star International Ltd.
-----------
T001 FIC00400213
T001 ROM0020670
Creating VARRAY as PL/SQL Type :The maximum size of a VARRAY is 2 gigabytes (GB) as in nested tables which is
discussed later. In this case, the elements of a VARRAY are stored contiguously in
memory and not in the database.
Example:
TYPE location_type IS VARRAY(4) OF Member.carea%TYPE;
address location_type;
/
Type created.
DECLARE
TYPE location_type IS VARRAY(3) OF Member.carea%TYPE;
address location_type;
table_count NUMBER;
BEGIN
address:=
location_type(Bedminster,Allenbury,Ridgeland,Rockland
);
table_count := address.count();
FOR i in 1..table_count
LOOP
89
SQL Star International Ltd.
DBMS_OUTPUT.PUT_LINE(address(i));
END LOOP;
END;
/
In the above code, the size of a VARRAY is restricted to 4. You can initialize a
VARRAY by using constructors. If you try to initialize the VARRAY with more than
four elements, then a Subscript outside of limit error message is displayed.
If you want more flexibility when working with Composite Datatypes, you can use
Nested tables.
90
SQL Star International Ltd.
Nested Tables
As you have seen, varying arrays have a limit on the maximum number of values
that can be entered. In case you cannot define a maximum number of values to be
entered into a record, you can use nested tables. It can be created either as a
schema object or as a PL/SQL type.
A nested table is a table within a table, just as a nested query or a subquery is a
query within a query.
column_name>
STORE
AS
Where,
CREATE TABLE <table_name> creates a relational table specifying the table name.
<column_name> datatype(size) are columns defined with an Oracle provided
datatype.
<column_name> abstract datatype are columns defined using abstract datatypes,
that are table types.
91
SQL Star International Ltd.
92
SQL Star International Ltd.
ELEM_TYPE_NAME
------------TYPBOOKDETA
ELEM_TYPE_OWNER
--------------SCOTT
Displaying Data
Nested table as you have seen is a column within a table.
93
SQL Star International Ltd.
Oracle provides a special keyword, THE, to query from nested tables. You cannot
query a nested table with a SELECT statement.
You need to enclose the SELECT query that selects the nested table column from the
table, within THE keyword. This statement is then used as the source for the FROM
clause of another SELECT query to extract the required vale from the nested table.
The syntax to query from nested tables is as follows:
THE (SELECT nested table column
FROM <table_name>
WHERE column_name = value);
If you want to know the details of the books borrowed by Jane Willow you need to
execute the following code:
SELECT *
FROM THE (SELECT Books
FROM MemberBook
WHERE MemberName = Jane Willow);
On executing the code, the following result is displayed:
BOOKID
BOOKC
AUTHOR
------
-----
------
FIC00400213
Fic
Robin Cook
ROM002067
Rom
Danielle Steel
ROM002099
Rom
Danielle Steel
CLS002067
Cla
Mark Twain
94
SQL Star International Ltd.
This query selects the name of the member from the MemberBook table. The
query should now be made the SELECT query for the INSERT, to add the new
members issue transaction.
INSERT INTO MemberBook
SELECT Kety,nt.BookID,nt.BookCategory,nt.Author
FROM THE (SELECT Books FROM MemberBook
WHERE MemberName=Jane Willow) nt;
INSERT INTO MemberBook
*
ERROR at line 1:
ORA-00913: too many values
This statement fails because the statement attempts to insert more values than
there are columns in the MemberBook table. The MemberBook table has two
columns. Though one of the columns is a nested table, you cannot directly insert
values into its columns in the manner done above. Hence, the statement needs to be
rewritten using two keywords, CAST and MULTISET.
CAST represents the result of a query as a nested table and MULTISET allows the
CAST query to have multiple rows. These two keywords can be used together.
Now the INSERT transaction you want to perform can be done as follows:
INSERT INTO MemberBook
VALUES (James Willow,
CAST (MULTISET (SELECT *
FROM THE (SELECT Books FROM
MemberBook
WHERE MemberName =
Jane Willow)) AS
BookDetaNest));
1 row created.
To confirm the insertion of a row, you can query the MemberBook table as follows:
SELECT nt.Author
FROM THE (SELECT Books FROM MemberBook
WHERE MemberName = James
Willow) nt;
The query displays the following result set:
95
SQL Star International Ltd.
AUTHOR
Danielle Steel
Danielle Steel
Mark Twain
Robin Cook
Nested tables are very powerful in their functionality. With a combination of the
keywords provided you could perform queries and other data manipulations on
nested tables and also use them with commands to manipulate the main tables.
type _name
IS TABLE OF
The following program illustrates the use of PL/SQL type Nested table.
96
SQL Star International Ltd.
Output
Bedminster
Allenbury
Ridgeland
Roxbury
PL/SQL procedure successfully completed.
97
SQL Star International Ltd.
type_r_cursor;
Book_rec Book%rowtype;
BEGIN
OPEN c_book FOR
98
SQL Star International Ltd.
LOOP
FETCH
EXIT WHEN
c_book%rowcount> 20 or c_book%notfound ;
DBMS_OUTPUT.PUT_LINE(book_Rec.cBookname || - ||
book_Rec.cAuthorName);
END LOOP;
CLOSE c_book;
END;
/
Type type_r_cursor is REF CURSOR;
The above statement simply defines a new data type called type_r_cursor, which is
of the type REF CURSOR. We declare a cursor variable named c_BOOK based on
the type type_r_cursor as follows:
c_book
type_r_cursor;
A cursor variable can be associated with more than one query at runtime.
Summary
In this chapter, you have learnt that:
Composite datatypes (also known as collections) store multiple data of different
datatypes due to its internal logical division.
99
SQL Star International Ltd.
100
SQL Star International Ltd.
Lab Exercises
1. Create a PL/SQL block to display the information about a given job, say
ST_CLERK.
2.
Write a PL/SQL block to print information about a given country using PL/SQL
Record.
a. Use the DEFINE command to provide the country ID. Pass the value to the
PL/SQL block through a iSQL*Plus substitution variable.
SET SERVEROUTPUT ON
SET VERIFY OFF
DEFINE p_countryid=CA
b. Use DBMS_OUTPUT.PUT_LINE to print information about country details.
C. Execute and test the PL/SQL block for the countries with the Ids CA,DE,UK,US.
3.
Create a PL/SQL block that will store the information of a retired employee
101
SQL Star International Ltd.
Retrieve the record of the employee specified and store it in the variable
declared
[Note: Create the RetiredEmpsData table with the columns: EmpID, EName, Job,
MgrID, HireDate, RetiredDate, Sal, Comm, and DeptID]
4.
Create a PL/SQL block that uses INDEX BY table to retrieve the names of
cities of each location ID from the Locations table, and print the same on
the screen. The block should do the following:
Retrieve the names of all current cities from the Locations table into the
INDEX BY table using a loop. Assign value for the Location_ID column based
on the following counter values:
102
SQL Star International Ltd.
Use another loop to retrieve the city names from the INDEX BY table and
print them using the DBMS_OUTPUT.PUT_LINE
Roma
Venice
Tokyo
Hiroshima
Southlake
South San Francisco
South Brunswick
Seattle
103
SQL Star International Ltd.
Toronto
Whitehorse
Beijing
Bombay
Sydney
Singapore
London
Oxford
Stretford
Munich
Sao Paulo
Geneva
Bern
Utrecht
Mexico City
5.
6.
Create an Object type for employee ID. Use this in the creation of the Varray
7.
Create a nested table column to hold the details of the projects handled by
each employee. Insert a few record into this table and confirm the records inserted
by querying them.
104
SQL Star International Ltd.
Chapter 5
105
SQL Star International Ltd.
Objectives
At the end of this chapter, you will be able to:
Differentiate between Implicit and Explicit Cursors
Use explicit cursors
Test the cursor status
Write cursors using parameters
Ensure faster update during cursor processing
106
SQL Star International Ltd.
Implicit Cursors
The Oracle server creates a work area implicitly when any SQL statement is issued
within its executable section. It is known as an Implicit Cursor. It is called implicit
because it is created and managed by the server automatically. The programmer
need not create it.
But what purpose do these implicit cursors serve?
On executing a SELECT statement within a PL/SQL block, there are a lot of issues the
programmer would be concerned of. For instance, if a SELECT statement within the
block does not retrieve any records, it displays the message stating no data found.
This situation can be handled by using some attributes that help programmers
evaluate what must have happened when the implicit cursor was used.
Implicit cursors are called SQL cursors, as the user does not specifically create them.
You cannot control the functioning of implicit cursors but can view the information
about the latest cursor by using implicit cursor attributes.
Attributes
SQL%ROWCOUNT
Purpose
Returns the number of rows retrieved by the most recently
Issued SELECT statement.
SQL%FOUND
SQL%NOTFOUND
SQL%ISOPEN
The use of these attributes will be clear after you try out these examples.
If the librarian wants to know the number of rows that got updated when he changed
the number of copies of Alien Legacy stored in branch 02CHSNJ and 03HAMNJ,
he will have to use SQL%ROWCOUNT as follows:
107
SQL Star International Ltd.
108
SQL Star International Ltd.
Fetch and process more than one row returned by a query, one at a time
A difference between an implicit and explicit cursor is that although implicit cursor
performs an array fetch, the existence of a second row always raises the exception
TOO_MANY_ROWS. Explicit cursors can be used to fetch multiple rows and reexecute parsed queries in the work area.
Explicit Cursors
Developers use explicit cursors in order to process the results of the SQL statements
by manipulating individual records in the result set.
Follow these steps to use an explicit cursor:
Declaring a Cursor
A cursor is declared in the declarative section of the PL/SQL block. Syntax for
declaring a cursor is:
CURSOR <cursor name> IS
SELECT statement
DECLARE
109
SQL Star International Ltd.
vBkName Book.cBookName%TYPE;
CURSOR curCategoryBooks IS
SELECT DISTINCT (cBookName)
FROM Book
WHERE cCategoryID=01FIC
AND cBranchID=02CHSNJ;
This code declares a cursor curCategoryBooks, which defines a SELECT statement
to retrieve books of fiction category at the Chester branch. A variable vBkName is
also declared which holds the values fetched by the cursor.
Remember two points while declaring cursors:
Variables used within the SELECT statement in the cursor must be declared
before the cursor is declared.
Do not use the INTO clause while declaring the cursor as it is used in the
FETCH statement.
Opening a Cursor
You invoke a cursor by opening it. A cursor comes into existence only when you open
it and not when you declare it. A cursor is opened in the executable section of a
PL/SQL block. The syntax to open a cursor is:
OPEN <cursor name>
110
SQL Star International Ltd.
The record pointer is moved to the next row in the active set.
The output variables are set with values retrieved from the cursor.
The records fetched are then processed using SQL and PL/SQL statements. The
values retrieved from the query, must have corresponding variables of compatible
datatypes in the INTO statement.
The FETCH statement works with two kinds of variables:
Stand-alone variables that are used to store the values of each column of the
record fetched by the cursor. The values are set depending on positional
specifications.
Declared records that contain the same set of attributes as the columns
defined by the cursor. The order of columns in the declared record type and
the columns defined in the cursor must be the same as the cursor values are
stored in the declared record type based on positional specification.
The cursor you have just declared and opened is ready to fetch rows one by one into
the variable declared. The following code will illustrate this:
LOOP
FETCH curCategoryBooks INTO vBkName;
Since the explicit cursor processes multiple rows by fetching one row at a time, you
need to therefore place the FETCH statement within a loop.
111
SQL Star International Ltd.
Ensure that the variables in the INTO clause of the FETCH statement is of the
same number and datatype as that of the SELECT statement.
If there are no rows left to process, then FETCH will not retrieve any values.
112
SQL Star International Ltd.
%ISOPEN
:-
%ROWCOUNT :-
%NOTFOUND :-
Now that you know how cursors are declared and what its various attributes are, you
need to complete the code wherein you had declared the cursor curCategoryBooks.
This is how it can be done:
DECLARE
vBkName Book.cBookName%TYPE;
CURSOR curCategoryBooks IS
SELECT cBookName FROM Book
WHERE cCategoryID = 01FIC
AND cBranchID = 02CHSNJ;
BEGIN
113
SQL Star International Ltd.
However, this requirement could be met using explicit cursors and its attributes.
Therefore, the library database developer decided to generate the above code.
In the above code, curCategoryBooks cursor retrieves all the fiction books that are
stored in the Chester branch. Out of the total number of fiction books in the Chester
branch, the requirement is to fetch only two of them. This is achieved by using the
%ROWCOUNT attribute.
On executing the above code you get the following result:
Triumph Of Katie Byrne
Woman Of Substance
Parameters in Cursors
Cursors get values in the SELECT statement from a table with hard-coded values.
However, at times you may have to reuse the cursors and it is quite tedious to
rewrite the cursor structure. Therefore, you need to use a method by which you can
specify where the data needs to be picked up from. This should be specified when
the cursor is opened. This method is implemented by the use of parameters in
cursors.
Parameters are variables used to pass values to a block during runtime. Parameters
pass values to the cursor when the latter is opened and the values are used in a
query. This allows you to open and close a cursor many times in a block. Each time
you pass a different set of values you get the appropriate results. Every parameter in
the cursor must have a corresponding parameter in the OPEN statement. Datatypes
for cursor parameters are the same as those for scalar variables, but you cannot
specify a size for the parameter when declaring it. The parameter names are used in
the query expression in the cursor for referencing.
114
SQL Star International Ltd.
datatype is the datatype for the parameter. Width should not be specified.
SELECT statement is the query associated with the cursor where the parameters are
used.
Values are passed to the cursor when you open it. You can use PL/SQL variables,
host variables or literals to pass values to the parameters. You can also set high and
low levels to parameters allowing the cursor to select data within the range.
Parameters in cursors also allow you to have more control over the processing of the
data.
For instance, the library desk officer is frequently faced with enquiries requiring him
to retrieve a list of all the books belonging to particular categories. To help him
perform this task the database developer generates the following code wherein the
desk officer is required to feed in only the category name based on which the list of
books belonging to the category specified are displayed. This code makes use of a
parameterized cursor where the parameter takes in the category name.
DECLARE
vBookName Book.cBookName%TYPE;
vCatgName Category.cCategoryName%TYPE:=&catname;
CURSOR curCategoryBooks (Catgname CHAR)IS
SELECT DISTINCT (cBookName)
FROM Book B,Category C
WHERE B.cCategoryID = C.cCategoryID
AND C.cCategoryName = vCatgName;
BEGIN
OPEN curCategoryBooks(vCatgName);
LOOP
FETCH curCategoryBooks INTO vBookName;
EXIT WHEN curCategoryBooks%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(vBookName);
END LOOP;
CLOSE curCategoryBooks;
END;
In this code a substitution variable catname is declared, which prompts the user for
a category name every time the code is executed. The value entered is stored in
vCatName and then passed into the cursor parameter Catgname when the cursor
is opened. Accordingly, the books belonging to the category entered are retrieved by
the cursor.
115
SQL Star International Ltd.
DECLARE
vMemberID Member.cMemberID%TYPE:=&memid;
CURSOR curNonMember IS
SELECT cMemberID,cFirstName,cLastName,cPhone,
dMembershipDt,cBranchID
FROM Member;
recNonMember curNonMember%ROWTYPE;
BEGIN
OPEN curNonMember;
LOOP
FETCH curNonMember INTO recNonMember;
EXIT WHEN curNonMember%NOTFOUND;
IF recNonMember.cMemberID=vMemberID THEN
INSERT INTO NonMember
116
SQL Star International Ltd.
VALUES
(sqSerialNo.NEXTVAL,
recNonMember.cMemberID,
recNonMember.cFirstName,
recNonMember.cLastName,
recNonMember.cPhone,
recNonMember.dMembershipDt,
recNonMember.cBranchID);
END IF;
END LOOP;
CLOSE curNonMember;
END;
/
On execution of the code, you are prompted to enter the ID of the member whose
membership has been terminated in the following way:
Enter value for memid: APP039601
PL/SQL procedure successfully completed.
You can verify whether the details of member with ID APP039601 has been inserted
into the NonMember table by issuing a SELECT statement as follows:
SELECT *
FROM NonMember
WHERE cMemberID=APP039601';
CLASTNAME CPHONE
DMEMBERSH CBRANCH
--------
--------- ------
--------- -------
27
--------- ---------APP039601
Perry
Paine
117
SQL Star International Ltd.
Opening
Parsing
Executing
Declaring variables to handle the fetched data (thereby reducing the size of
the declaration section)
Do not declare the record that controls the loop, as its scope is limited to
within the loop.
Test the status of the cursor attributes during the loop if required.
Use cursor parameters by defining them in parentheses after the cursor name
in the CURSOR FOR loop.
If the cursor operations need to be done manually, then do not use the
CUSOR FOR loop.
Specify the SELECT statement instead of the cursor name in the CURSOR FOR
loop. This SELECT statement is an explicit cursor without a name and hence
its attributes cannot be tested. The scope of the cursor in this case is limited
to the CURSOR FOR loop.
118
SQL Star International Ltd.
The usage of CURSOR FOR loop is to accomplish the three major processes (opening,
fetching and closing) of a cursor in a single step. Use CURSOR FOR loop to rewrite
the code written to retrieve fiction books belonging to the Chester branch.
DECLARE
CURSOR curCategoryBooks IS
SELECT cBookName FROM Book
WHERE cCategoryID=01FIC
AND cBranchID=02CHSNJ;
BEGIN
FOR recCatgBooks IN curCategoryBooks
LOOP
DBMS_OUTPUT.PUT_LINE (recCatgBooks.cBookName);
END LOOP;
END;
/
Wings Of The Storm
Triumph Of Katie Byrne
Woman Of Substance
PL/SQL procedure successfully completed.
119
SQL Star International Ltd.
cursor, no other user must be able to access it. Two clauses are used for this
purpose:
Gives applications more control over the wait time for locks
120
SQL Star International Ltd.
would
not
appreciate
You can specify an integer along with the FOR UPDATE WAIT clause to specify the
number of seconds to wait for a lock. Once the number of seconds specified elapses,
and if the server is unable to obtain the lock within that time interval, it returns the
following error:
ORA-30006: resource busy; acquire with WAIT timeout expired.
The library database developer may need to generate a code for the purpose of
updating the number of copies of books. But to avoid the number of copies from
being updated by other users at the same time, he decides to lock it using the FOR
UPDATE OF clause in the following way:
DECLARE
CURSOR curBranchBkCopies IS
SELECT cBookName,nNoOfCopies
FROM Book
WHERE cBranchID=04RANNJ
FOR UPDATE OF nNoOfCopies WAIT 30;
In this code, the developer declares a cursor that selects the number of copies of
books stored in a specified branch. But the developer locks the rows retrieved by the
cursor as he intends to update the number of copies. This prevents others from
updating the same rows. In the code, the query waits for 30 seconds to obtain a
lock.
121
SQL Star International Ltd.
Some points to remember when using the WHERE CURRENT OF clause are:
The cursor containing query statement should be declared with the FOR
UPDATE clause.
You can use the clause in the DELETE or UPDATE statements to refer to the
latest row processed by the FETCH command.
122
SQL Star International Ltd.
Subqueries in Cursors
Subqueries are generally used in the WHERE clause of a query. You can also use
them in the FROM clause. In the latter case, it results in a temporary data source for
the query. Subqueries in cursors function the same way as they do when used in a
query not involving cursors.
For example, the library management wishes to have a report that displays a list of
those members who have paid a fine amount more than the average fine received on
the same issue date as that of the member. This could be achieved by declaring the
following cursor:
DECLARE
CURSOR curIssDtFine IS
SELECT T.dIssueDt IssueDt,T.cMemberID MemberID,T.cBookID
BookID,T.nFine Fine, TR.FineAvg FineAvg
FROM Transaction T,(SELECT dIssueDt,
AVG (nFine) FineAvg
FROM Transaction
GROUP BY dIssueDt) TR
WHERE T.dIssueDt = TR.dIssueDt AND
T.nFine > TR.FineAvg;
recIssDtFine curIssDtFine%ROWTYPE;
BEGIN
OPEN curIssDtFine;
LOOP
FETCH curIssDtFine INTO recIssDtFine;
EXIT WHEN curIssDtFine%NOTFOUND;
DBMS_OUTPUT.PUT_LINE(recIssDtFine.IssueDt||
||recIssDtFine.MemberID||
||recIssDtFine.BookID||
||recIssDtFine.Fine||
||recIssDtFine.FineAvg);
END LOOP;
123
SQL Star International Ltd.
CLOSE curIssDtFine;
END;
/
This code declares a cursor curIssDtFine, which selects the members IDs along
with the date on which the books are issued. But the cursor only selects those
members who have paid a fine amount greater than the average fine received by the
library on each issue date. This is achieved by using subquery in the FROM clause.
The subquery returns the average fine received on each issue date. The outer query
compares the fine amount of each member against the average fine calculated.
6 4.5
7.5 6
6 3.75
10.5 6.75
12 6.75
6 4.5
Summary
In this chapter, you have learnt that:
Cursors are private work areas used to execute SQL statements. They are of
two types.
1. Implicit cursor are created, maintained by the server and named internally as
SQL. Its status can be checked using the attributes SQL%ISOPEN,
SQL%FOUND, SQL%NOTFOUND and SQL%ROWCOUNT.
124
SQL Star International Ltd.
2. Explicit cursors defined and controlled by the user have same attributes as
implicit cursors but prefixed by name given by the user.
E.g. Emp_Cursor%NOTFOUND
Modification in the table through cursor is done through FOR UPDATE and
WHERE CURRENT OF clauses.
FOR UPDATE clause locks the rows to be updated and WHERE CURRENT OF
points to the row currently being referred to, by the cursor.
125
SQL Star International Ltd.
Lab Exercises
1.Create a PL/SQL block that deletes the data from employees table based
on
the
2.Create a PL/SQL block that will retrieve the first 5 employees one after the other.
[Note: You need to create a table TopEarners. The
as shown below.]
3.Create a PL/SQL block that will determine the top 10 earners of the organization.
The block should:
Gather the salaries of the top 10 earners within a loop. Ensure that salaries
are not duplicated.
Store the salaries gathered in the TopEarners table
4.Create a PL/SQL block to retrieve the list of employees (their first name and
department number) working for the IT department (department ID is 60).
5.Create a PL/SQL block that will retrieve the first name, job ID, and salary of
employees belonging to department number 80. If the salary is less than $10,000
and if job ID is SA_REP then display the message <First name> is due for a salary
raise, or else display the message <First name> is not due for any salary raise as
shown below.
126
SQL Star International Ltd.
7.
Create a PL/SQL block that will retrieve the department ID and department
name of departments whose ID is less than 80. Pass the department IDs retrieved as
parameters to another cursor that would retrieve the first name, hire date, job ID
and salary of employees belonging to that department and whose employee ID is
less than 130. The block result should be as follows:
ID: 121 Name: Adam Job: ST_MAN Hired on: 10-APR-97 earns: 8200
127
SQL Star International Ltd.
..
Dept No.: 60 Dept Name: IT
ID: 103 Name: Alexander Job: IT_PROG Hired on: 03-JAN-90 earns:
9000
ID: 104 Name: Bruce Job: IT_PROG Hired on: 21-MAY-91 earns: 6000
8.
Create a PL/SQL block that will increase the salary by 10% of employees
whose salary is less than $7000 and who belong to department number 50. [Hint:
Use FOR UPDATE and WHERE CURRENT OF clause].
128
SQL Star International Ltd.
Chapter 6
129
SQL Star International Ltd.
Objectives
At the end of this chapter, you will be able to:
Describe the different types of Exceptions
Raise and Handle Exceptions
130
SQL Star International Ltd.
Introduction
There can be situations when a PL/SQL code might generate a runtime error. This
error in PL/SQL is known as an Exception which is an identifier that is raised when it
encounters an unacceptable situation during the execution thereby causing the block
to terminate. The exceptions are handled by a special code called exception
handlers.
The activities that are included when working with exceptions are:
Declaring an exception
Raising an Exception
Trapping and Handling the Exception
Propagating the Exception
Let us look into each of them in detail.
Declaring Exceptions
Exceptions are also known as Named Identifiers. These are declared in the
declarative section of the PL/SQL block.
Syntax for declaring Exceptions:
DECLARE
exception_name EXCEPTION;
Raising Exceptions
An exception is raised when unacceptable situations are encountered. It can be
raised implicitly or explicitly.
Syntax for raising an exception explicitly using the RAISE keyword:
BEGIN
.............
RAISE exception_name;
Trapping Exceptions
Exceptions are trapped when the control encounters a RAISE EXCEPTION in the
executable section of a PL/SQL code. Then the control is given to the corresponding
131
SQL Star International Ltd.
exception handler in the exception section of the block. After an exception is handled
it terminates the PL/SQL code and leaves the block.
The syntax to trap an exception is:
EXCEPTION
WHEN exception1 [or excpetionN. . .] THEN
statement1;
statement2;
[WHEN excetpion 2 [or exceptionN . . .] THEN
statement1;
statement2;
. . .]
[WHEN OTHERS THEN
statement1;
statement2;
. . .]
Where,
exception1 exceptionN are the names of the exceptions handler.
statement1 . . . are SQL or PL/SQL statements
OTHERS is a clause that traps unspecified exceptions. This is an optional clause.
Only the exceptions specified in the executable section of the block can be handled
by the exception handlers. The exceptions not specified in the executable block are
handled by the WHEN OTHERS clause. Hence, this clause is always placed last. All
untrapped exceptions are trapped with this clause.
Begin that part of the PL/SQL block with the keyword EXCEPTION.
Define handlers within the block with their respective set of actions.
PL/SQL executes only one exception handler before terminating the block.
The OTHERS clause is to be placed after all the exception handlers are coded.
There can only be one OTHERS clause.
132
SQL Star International Ltd.
Propagating Exceptions
When the block that raised the exception handles it, the block terminates as per the
instructions in the handler. Then the END statement of the block is encountered and the
control is given back to the enclosing block for execution.
In case of Nested blocks, when an exception is raised and there is no corresponding
handler within the block, the control is shifted to successive enclosing blocks in order of
execution. When the control is passed to these blocks the executable transactions of that
block are skipped. The advantage of doing this is, you can code handlers for specific
exceptions within the block. The enclosing successive blocks can handle the general
exceptions. If the exception is not handled by any of these blocks then the control is
passed on to the calling or host environment. All the other blocks are terminated.
Given below is a list of calling environments and the messages they generate when they
have to handle an exception.
The SQL * PLUS environment displays the error number and its message on
screen.
The Procedure Builder also displays the error number and its message.
In Oracle Developer Forms, the error number and message is trapped by the
in-built functions ERROR_CODE and ERROR_TEXT in a trigger.
A pre-compiler application accesses the error number using the SQLCA data
structure.
Finally, an enclosing PL/SQL block traps the exception in a handler in its exception
handling section.
Types of Exceptions
The three types of exceptions are:
Predefined exceptions
User-defined exceptions
Non-predefined exceptions
Predefined Exceptions
While writing SELECT statements you would have encountered errors like too many
rows or no data found. This is an error generated by the Oracle server. Errors like
these are trapped and handled by the server itself. These errors are called
Predefined Exceptions, as these are already defined within the server. These errors
occur due to some common situations in a database. Instead of you invoking and
trapping these errors explicitly, they are done automatically. A list of some of the
133
SQL Star International Ltd.
common pre-defined exceptions is given below in a table, along with their error
number and what the error implies.
134
SQL Star International Ltd.
When predefined errors occur, if you want your code to execute in a different
manner then code must be included in the exception section. For instance, the desk
officer of the
New Jersey Central Library handles frequent enquiries pertaining to books written by
particular authors by querying the Book table. But to save him the task of querying
the table repeatedly, the library database developer decides to embed the requisite
query into a
PL/SQL block. The database developer writes a code that will retrieve books written
by the author whose name the desk officer is prompted to enter. But while writing
such a code, the database developer needs to handle commonly encountered
exceptions such as NO_DATA_FOUND or TOO_MANY_ROWS and word them in a way
that would make it easy for the desk officer to interpret. The following code
illustrates this:
DECLARE
vAuthorName Book.cAuthorName%TYPE:=&author;
vBkName Book.cBookName%TYPE;
BEGIN
SELECT cBookName
INTO vBkName
FROM Book
135
SQL Star International Ltd.
WHERE cAuthorName=vAuthorName;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE(more than one book written by
author||- ||vAuthorName);
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE(no book written by author||||vAuthorName);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE(An
the author
error
has
occurred:
check
END;
/
This code returns easily understandable error messages on being executed. Such as:
An error has occurred: check the author name you have just entered: If
any other error such as misspelling an authors name or entering a name that
does not exist in the database table.
However, instead of displaying your own error message in the WHEN OTHERS clause,
you could easily identify the error code and error message associated with errors (other
than NO_DATA_FOUND and TOO_MANY_ROWS) using the following two functions:
SQLERRM, which returns the message associated with the error number
136
SQL Star International Ltd.
Using the two functions, the WHEN OTHERS clause of the above code could be
rewritten. But you need to declare two variables to hold the values returned by the
functions in the following way:
DECLARE
vErrCode NUMBER;
vErrMssg VARCHAR2(255);
vAuthorName Book.cAuthorName%TYPE:= &author;
vBkName Book.cBookName%TYPE;
BEGIN
SELECT cBookName
INTO vBkName
FROM Book
WHERE cAuthorName=vAuthorName;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
DBMS_OUTPUT.PUT_LINE(more than one book written by
author||- ||vAuthorName);
WHEN OTHERS THEN
vErrCode:= SQLCODE;
vErrMssg:= SQLERRM;
DBMS_OUTPUT.PUT_LINE(vErrCode||-||vErrMssg);
END;
/
On executing the above code, the functions SQLCODE and SQLERRM return the error
number and error message respectively on encountering an authors name whose books
are not stored in the New Jersey Central Library.
Enter value for author: Jane Austen
100-ORA-01403: no data found
The Oracle server provides another method (other than using
DBMS_OUTPUT.PUT_LINE) to communicate predefined exceptions interactively. The
method uses RAISE_APPLICATION_ERROR procedure. This procedure returns a
non-standard error code and error message because they are user-defined rather than
137
SQL Star International Ltd.
system defined. Therefore, this procedure returns error messages that are consistent with
other Oracle server errors. The syntax for using RAISE_APPLICATION_ERROR is:
RAISE_APPLICATION_ERROR (error_number, error_message);
Where,
error_number is a number the user specifies for the exception. The number specified
must be between -20000 and 20999.
error_message is a message the user specifies for the exception. It must be a character
string and can be up to 2,048 bytes.
The above code could be rewritten using RAISE_APPLICATION_ERROR as follows:
DECLARE
vAuthorName Book.cAuthorName%TYPE:=&author;
vBkName Book.cBookName%TYPE;
BEGIN
SELECT cBookName
INTO vBkName
FROM Book
WHERE cAuthorName=vAuthorName;
EXCEPTION
WHEN TOO_MANY_ROWS THEN
RAISE_APPLICATION_ERROR(-20002,More than one book
written by author||||vAuthorName);
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20111,No book written by
author||vAuthorName);
WHEN OTHERS THEN
DBMS_OUTPUT.PUT_LINE (An error has occurred: check
the
Predefined exceptions are handled implicitly. However, you might want to handle an
exception explicitly. An exception that is raised and handled explicitly is called userdefined exception. User-defined exceptions are used to implement business rules that are
not trapped by the server. In case of user-defined exceptions, you need to write code in all
the sections of the block:
Declare the exception in the declarative section. The syntax for declaring an
exception is:
<exception_name> EXCEPTION, where exception_name is the name of the
exception.
In the execution section, write the code for the exception and raise it if the
test conditions are met. The syntax to raise an exception is:
RAISE exception_name, where exception_name is the name of the exception
declared.
In the exception handling section, include the WHEN clause, the name of the
exception and the code to handle it. When the exception is raised, control of
the block is transferred to this section. Include the WHEN OTHERS clause so
that it can catch all those exceptions that do not have a handler within the
code.
The following example shows how to handle exceptions that are not trapped by the
server.
A desk officer enters an invalid author name while updating the number of copies of their
books. While doing this, an exception is raised. To trap this exception, he would first
declare an exception, raise the exception and then handle it.
DECLARE
exInvalidAuthor EXCEPTION;
vAuthorName Book.cAuthorName%TYPE:=&author;
BEGIN
UPDATE Book
SET nNoOfCopies=nNoOfCopies+1
WHERE cAuthorName=vAuthorName;
IF SQL%NOTFOUND THEN
RAISE exInvalidAuthor;
END IF;
139
SQL Star International Ltd.
EXCEPTION
WHEN exInvalidAuthor THEN
DBMS_OUTPUT.PUT_LINE(The author name you have entered is
not
a valid one);
END;
/
On executing the code, if the desk officer enters a name not listed in the Book table, he
would get an error.
The error is handled by declaring a user-defined exception exInvalidAuthor in the
declarative section, which is raised when the embedded query returns no rows.
exInvalidAuthor raised is then handled in the exception section, which displays the
following error message:
The author name you have entered is not a valid one
author
name
you
On entering an invalid authors name, you get the following error message:
Enter value for author: Ayn Rand
ORA-20201: the author name you have entered is not a
valid one
140
SQL Star International Ltd.
Non-predefined Exceptions
You have seen how to handle an exception explicitly. You might want to customize
the predefined exceptions and thus extend the list. These errors are associated with
predefined errors. You can customize the predefined errors by assigning names to
the error number and writing code to handle these exceptions. You can implement
this by using the
PRAGMA EXCEPTION_INIT keyword. This statement is a compiler directive. It
instructs the compiler to associate the exception name given with the Oracle error
number. When you use this statement, you need not raise the exception. You need
to declare it and write exception handling code. Each time an error occurs during
execution, the control is shifted to the exception section and the error is handled.
There are situations where non-predefined exceptions(also known as Internal
Exceptions) need to be handled. For instance, whenever the database users of the
New Jersey Central Library try to delete any members details from the Member
table the server displays an error message, indicating an integrity constraint
violation. This is because the foreign key column in the Transaction table is
referencing the cMemberID column of the Member table. But the error message
displayed is not very descriptive. Also, there is no named predefined exception to
handle this. Therefore, the database developer should declare his own exception and
associate it with the Oracle error number for integrity constraint violation.
DECLARE
exMemberIssBks EXCEPTION;
PRAGMA EXCEPTION_INIT(ExMemberIssBks,-2292);
vMemberID Member.cMemberID%TYPE:=&memberid;
BEGIN
DELETE FROM Member
WHERE cMemberID = vMemberID;
COMMIT;
EXCEPTION
WHEN exMemberIssBks THEN
DBMS_OUTPUT.PUT_LINE (Cannot remove the details of member
with ID ||vMemberID||because books issued by him have not
yet been returned);
END;
/
Enter value for memberid: CDB028504
141
SQL Star International Ltd.
In this code, an exception exMemberIssBks is declared and associated with the Oracle
server error number 2292 (integrity constraint violation) using PRAGMA
EXCEPTION_INIT. Any attempt to delete details of a member raises the exception
implicitly. The control is passed to the exception handling section and the following
message is displayed:
Cannot remove the details of member with ID CDB028504 because
books issued by him have not yet been returned.
THEN
142
SQL Star International Ltd.
Performing Calculation
Cannot divide by zero
PL/SQL procedure successfully completed.
The output gives you a message before handling the error and after handling the
error.
Let us now modify the code a bit. Suppose your requirement is to view the
information about all the oracle defined errors raised during the execution of this
program along with your messages, then this is solved by using
DBMS_UTILITY.FORMAT_ERROR_STACK. This package actually gives you the
information about the errors raised, which are stored in the form of Stack.
CREATE OR REPLACE PROCEDURE my_proc
IS
v_num1 NUMBER:=1;
v_num2 NUMBER:=0;
v_result NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE(Performing Calculation);
v_result:= v_num1/v_num2;
EXCEPTION
WHEN ZERO_DIVIDE
THEN
143
SQL Star International Ltd.
IS
v_num1 NUMBER:=1;
v_num2 NUMBER:=0;
v_result NUMBER;
BEGIN
DBMS_OUTPUT.PUT_LINE(calculation);
v_result:= v_num1/v_num2;
EXCEPTION
10
WHEN ZERO_DIVIDE
THEN
11
12
DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_ERROR_STACK);
13
DBMS_OUTPUT.PUT_LINE(DBMS_UTILITY.FORMAT_ERROR_BACKTRACE);
14*
END;
/
Procedure created.
SQL> EXEC my_proc
Performing Calculation
Cannot divide by zero
ORA-01476: divisor is equal to zero
ORA-06512: at SCOTT.MY_PROC, line 8
144
SQL Star International Ltd.
Summary
In this chapter, you have learnt that:
Exception are errors which arises during the program execution. They can be
handled in an exception block (EXCEPTION) or left unhandled.
Smooth termination, customized error message and transaction intact are the
advantages of handling exceptions.
145
SQL Star International Ltd.
Lab Exercises
1.
Create a PL/SQL block wherein a non-predefined error is displayed if a user
attempts to delete department number 40. This is because the department has employees
working in it.
2.
Create a PL/SQL block that declares a user-defined exception in case a user
updates the department name of a department that does not exist (that is, by passing a
department number that does not exist in the table).
146
SQL Star International Ltd.
Chapter 7
Creating Subprograms
Introduction to Subprograms
Invoking Subprograms
Creating Procedures
Effects of Handled and Unhandled Exceptions
Creating Functions
147
SQL Star International Ltd.
Objectives
At the end of this chapter, you will be able to:
Use Procedures
Use Functions
148
SQL Star International Ltd.
Procedures
Functions
Packages
Introduction to Subprograms
Subprograms are named PL/SQL blocks, which are based on a standard PL/SQL block
structure that comprises of a declarative section, an executable section and an
optional exception section.
Subprograms are compiled and stored in the database. This helps reduce network
traffic. No file is required to be passed to the server to execute the program logic. All
that is required for the user is to refer to the name of the PL/SQL block and execute
it.
Subprograms provide modularity. Modularization refers to the process of breaking
large blocks of code into smaller groups of code known as modules. You can create
modules so that they can be used in more than one application.
The advantages of developing subprograms are as follows:
Improving performance: Codes are parsed at compile time and not runtime.
It uses the shared SQL area to avoid re-parsing the data. Since the
commands are grouped together, there will be lesser number of calls to the
database and in this process the network traffic also gets reduced.
149
SQL Star International Ltd.
Block structure for PL/SQL subprograms and anonymous PL/SQL blocks differ. This is
illustrated in the following diagram:
From the above diagram you will observe that subprograms are made up of two
parts: subprogram specification and subprogram body.
The subprogram specification contains:
150
SQL Star International Ltd.
The mandatory executable section between the BEGIN and END keywords
The optional exception section between the EXCEPTION and END keywords
Invoking Subprograms
To make a Subprogram work, you need to invoke it. Environments that allow you to
invoke subprograms include other subprograms, pre-compiler applications, Oracle
Developer, Oracle Discoverer, Oracle WebDB and other Oracle tools.
In the list below are the ways in which you can call a subprogram:
Subprograms include both procedures and functions. Though the way in which they
are created is the same, they are slightly different in their functionality. You will
learn about these two individually in separate sections.
151
SQL Star International Ltd.
Procedures
Procedures are one of the subprograms used to perform some action. They may or
may not accept values from the users. They generally do not return a value.
A procedure has a header, a declarative section, an executable and an exception
handling section.
One of the main features promoted by procedures is reusability. Once a procedure is
validated, it can be used in any number of applications. In case the requirement
changes, you need to update only the procedure.
Creating Procedures
Once you have the permission to create a procedure use the following syntax to
create one:
CREATE [OR REPLACE] PROCEDURE <procedure_name>
152
SQL Star International Ltd.
153
SQL Star International Ltd.
END;
In the procedure procMember, the variables vMemID and vAge are formal
parameters.
Actual parameters refer to variables or expressions, which are referenced in the
parameter list of a subprogram call. For example, in the call procMember (vID, 26)
to the procedure procMember, the variable vID and 26 are actual parameters.
Points to remember:
During a subprogram call, actual parameters are evaluated and their results
are assigned to formal parameters.
Use different names for formal as well as actual parameters. It is only a good
coding practice.
IN: accepts a value from the calling environment and passes it to the
procedure. It can be assigned a default value. This parameter acts as a
constant. The values in it can be a constant or an expression. It is the default
mode.
OUT: passes a value from the procedure to the calling environment. It cannot
be assigned a Default value.
IN OUT: passes a value from the calling environment to the procedure and
returns a different value from the procedure into the environment using the
same parameter.
154
SQL Star International Ltd.
155
SQL Star International Ltd.
There are three ways in which you can pass parameters to procedures. They are:
Positional: Here, the user has to specify the values according to the position
of the parameter in the parameter list
Combination: Here, the first values are listed based on the position and the
rest of the values are listed using the special syntax of the named method
156
SQL Star International Ltd.
VARIABLE FName
CHAR(20)
VARIABLE LName
CHAR(20)
VARIABLE Address
VARCHAR2(50)
2. Invoke the procedure and supply the host variables as the OUT parameters.
EXECUTE prMemberDetails (CBW109702, :FName, :LName, :Address);
To view the values of the IN OUT parameter, invoke the prFormatPhone procedure
as follows:
2. Populate the host variable using an anonymous block and view the value entered
BEGIN
157
SQL Star International Ltd.
:phone_no := 0403730853;
END;
/
PRINT phone_no
3. Invoke the procedure and provide the host variable as the IN OUT parameter
EXECUTE prFormatPhone(:phone_no);
158
SQL Star International Ltd.
/
(123)456-7890
(123)456-7890
PL/SQL procedure successfully completed.
159
SQL Star International Ltd.
OR
REPLACE
PROCEDURE
prInsBrLocation
(pLocID
NUMBER,
pLocName VARCHAR2)
IS
LocName VARCHAR2(30);
BEGIN
DBMS_OUTPUT.PUT_LINE
(Main
Procedure
Calling
Procedure);
INSERT INTO BrLocation (nBrLocID, vLocName)
VALUES (pLocID, pLocName);
SELECT vLocName
INTO LocName
FROM BrLocation
WHERE nBrLocID = pLocID;
DBMS_OUTPUT.PUT_LINE
(LocName||
inserted
into
BrLocation
table);
160
SQL Star International Ltd.
prInsBranch(pLocID);
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE(No such branch/ location for the
New Jersey Central Library
END;
/
CREATE OR REPLACE PROCEDURE prInsBranch
IS
vBrID NUMBER(4);
BEGIN
DBMS_OUTPUT.PUT_LINE(Called Procedure);
INSERT INTO Branch
VALUES(06CALNJ, Elizabeth
Procedure prInsBrLocation
BrLocation table.
inserts
new
branch
location
into
the
161
SQL Star International Ltd.
The DML statements issued both in the calling as well as the called
procedures would be rolled back.
Removing Procedures
If you do not require any or all of your procedures, you can delete them just as it is
done for any other database object. You can drop the server-side procedures from
the iSQL*Plus environment by using the syntax:
DROP PROCEDURE <procedure_name>;
To remove the procedure prMemberDetails type the following command:
DROP PROCEDURE prMemberDetails;
162
SQL Star International Ltd.
SELECT TEXT
FROM USER_SOURCE
WHERE NAME=PRINSBRANCH;
To view the names of procedures presently existing in your schema, issue the following SELECT
statement.
SELECT OBJECT_NAME
FROM USER_OBJECTS
WHERE OBJECT_TYPE=PROCEDURE;
Functions
Functions are also stored subprograms and are usually used to calculate values. They
are created and invoked the same way as procedures but with a slight difference.
A function must compulsorily return a value to the calling environment.
163
SQL Star International Ltd.
Creating Functions
The syntax for creating functions is the same as that for procedures, except that the
keyword FUNCTIONS and RETURN are to be used. The syntax is:
CREATE [OR REPLACE] FUNCTION <function_name>
(argument1 [mode1] datatype1, argument2 [mode2] datatype2,. . .)
RETURN datatype
IS | AS
PL/SQL block;
Where,
CREATE [OR REPLACE] FUNCTION are the keywords that enable you to create a
function or replace an existing one.
<function_name> is the name of the function you want to create.
(argument1 [mode1] datatype1, argument2 [mode2] datatype2,. . .) are the
parameters that the function accepts from the calling environment.
RETURN datatype is the clause that is mandatory, as a function must return a value.
Do not specify a size for the datatype.
PL/SQL block is the set of SQL and PL/SQL statements to execute the task to be
performed. You must include a RETURN datatype in the executable part of the block.
You can use multiple RETURN statements within an IF statement. In this case,
however, only one RETURN statement will be executed.
164
SQL Star International Ltd.
vActReturnDt DATE;
BEGIN
SELECT dReturnDt,dActualReturnDt
INTO vReturnDt,vActReturnDt
FROM Transaction
WHERE cMemberID=vMemberID;
IF vReturnDt >= vActReturnDt THEN
vFine:=0;
ELSE
vFine:=(vActReturnDt - vReturnDt)* 1.50;
END IF;
DBMS_OUTPUT.PUT_LINE (The member has to pay a fine of:
||vFine);
RETURN(vFine);
END;
/
Invoking Functions
Functions are invoked from the iSQL*Plus environment using the EXECUTE
command.
You need to declare a host variable to store the value returned by the function. The
value can then be printed by passing the host variable name with the PRINT
command after executing the function. Execute it with the following syntax:
EXECUTE :host_variable_name
:=
function_name(value);
For example:
VARIABLE P NUMBER
EXECUTE :P := fnFineCal (BJH029405);
You can also invoke functions from SQL statements in case any ad hoc queries are to
be made. For example, if you want to see the name of a particular member and
phone number and the fine the member has to pay you can execute the following
statement:
SELECT cFirstName, cLastName, cPhone, fnFineCal(BJH029405) Fine
FROM Member
165
SQL Star International Ltd.
WHERE cMemberID=BJH029405';
The result of the above SELECT statement will return the following result:
CFIRSTNAME
CLASTNAME
CPHONE
--------
------
-------
Jessica
Hatcher
9642211309
FINE
------0
You can perform calculations that are very complex and not available in SQL
You can enhance data independence by not retrieving data into an application
and processing complex data in the server
As part of the CONNECT BY, START WITH, ORDER BY and GROUP BY clauses
Though there are several advantages of using functions in queries, they have some
restrictions. They are:
Accept only IN parameters with valid SQL datatype, not PLSQL specific types.
You need to have the EXECUTE privilege for the function so that you can
invoke it.
The function should not modify any table of the database with DML
statements. In other words, the function should not contain any DML
statements.
166
SQL Star International Ltd.
Removing Functions
Just as you can delete procedures, you can also delete functions. From the iSQL*Plus
environment you can delete functions by using the DROP command. In case you
want to delete the fnFineCal function that you created, execute the following
command.
DROP FUNCTION fnFineCal;
167
SQL Star International Ltd.
Summary
In this chapter, you have learnt that:
Subprograms are named and compiled PL/SQL blocks stored within the
database. They are compiled once and invoked many times.
168
SQL Star International Ltd.
Source code of the procedure and the function can be viewed in USER_SOURCE
data dictionary.
169
SQL Star International Ltd.
Lab Exercises
Note: Save all programs as .sql files.
1.
Create a procedure called prInsJobs to insert a new row into the Jobs table.
The procedure should accept two parameters, one for job ID and the other for job
title. Compile the procedure and invoke it in iSQL*Plus with IT_ADMIN as the job ID
and System Administrator as the job title. View the row inserted into the Jobs
table.
2.
Create a procedure called prRaiseSal, which increases the salary by 10% for
the employee ID accepted as the input parameter. Invoke the procedure in
iSQL*Plus with 110 as the employee ID. Prior to invoking the procedure, view the
salary earned by employee 110. After the successful execution of the procedure,
view the updated salary of employee 110.
3.
Create a procedure called prViewEmp that returns the job ID, department ID
and salary for a specified employee ID. Use host variables to view the OUT
parameters of the procedure in iSQL*Plus.
4.
Create a procedure called prUpdJobs to update the job title. The procedure
should accept job ID and job title. The procedure should also have necessary
exception handling in case no update occurs. Invoke the procedure and update the
job title of IT_ADMIN to SysDate Administrator. To check the exception handling,
try and update a job that does not exist.
5.
Create a procedure called prEmpRaiseSal that invokes the prRaiseSal
procedure (created in Lab question 02). The procedure prEmpRaiseSal must
process all the records in the Employees table, and then pass each employee ID to
the procedure prRaiseSal.
6.
Create a function called fnDept that returns the department name
corresponding to a department number passed as the input parameter. The function
should return the department name to a host variable defined in iSQL*Plus.
7.
Create a function called fnAnnualInc that returns the annual salary earned
by accepting monthly salary and commission. The function should return an annual
salary even if both the values passed are NULL. Annual salary is calculated as
follows:
(Salary * 12) + (Salary * Commission * 12)
Use the function in a SELECT statement against the Employees table.
170
SQL Star International Ltd.
Chapter 8
Managing Subprograms
System and Object Privilege Requirements
Definers Rights and Invokers Rights
Detecting Compilation errors
Displaying Parameters Using DESCRIBE Command
Debugging Program Units
171
SQL Star International Ltd.
Objectives
172
SQL Star International Ltd.
In order to create PL/SQL subprograms, you must have been assigned the system
privilege CREATE PROCEDURE. Once this privilege has been assigned, you can alter,
drop or execute the subprogram without any further privileges being assigned. If the
keyword ANY has been used, then you can create, alter, drop and execute not only
your subprograms, but also those belonging to other schemas. The keyword ANY is
optional only for the CREATE PROCEDURE privilege.
To invoke a subprogram if you are not an owner and do not have the EXECUTE ANY
system privilege, then you must have the EXECUTE object privilege assigned to you.
By default
PL/SQL subprograms execute under the security domain of the owner.
If subprograms refer to objects not in the same schema, then access to these
objects must be assigned explicitly, and not through a role.
173
SQL Star International Ltd.
The requirement is that Robert should to be able to access the Member table only
through the procMember procedure that Paul created. This requires the granting of both
direct and indirect access.
Direct Access
A user can be given access permissions directly on an object. For instance, from the
Library schema object privileges on the Member table can be provided to Paul.
Based on the object privilege, Paul can create the procMember procedure, which
queries the Member table.
Indirect Access
A user can access an object by using another object on which he has access
permissions. For instance, Paul grants the EXECUTE object privilege on the
procMember procedure to Robert. Robert can now execute the procedure under
the security domain of the owner [this is the definers right]. Robert can retrieve
information from the Member table using the procMember procedure because Paul
has direct privileges to Member table and has created the procedure.
174
SQL Star International Ltd.
Executing a subprogram from the security of the executing user, and not the owner,
is referred to as Invokers rights. To implement invokers right, use AUTHID
CURRENT_USER. This ensures that the subprogram executes with the privileges of
its current user. The following diagram depicts the procedure procMember, being
executed with the privileges of the user Robert:
175
SQL Star International Ltd.
iSQL*Plus commands:
Oracle supplied
information.
package,
DBMS_OUTPUT,
provides
run-time
debug
176
SQL Star International Ltd.
USER_OBJECTS View
A user or developer, like you, would typically want general information about all objects
created such as the name of the object, its owner, when was it created or what type of
object it is.
The USER_OBJECTS is a view that stores such information.
On querying the view, the following column list is displayed (we are providing you with
the abridged column list):
Issue the following SELECT statement to view all the procedures you have created:
SELECT OBJECT_NAME, OBJECT_TYPE
FROM USER_OBJECTS
WHERE OBJECT_TYPE=PROCEDURE
ORDER BY OBJECT_NAME;
/
177
SQL Star International Ltd.
OBJECT_NAME
OBJECT_TYPE
---------------
----------
PRACCEPTMSSG
PROCEDURE
PRFORMATPHONE
PROCEDURE
PRMEMBERDETAILS
PROCEDURE
PRTRANSMITMSSG
PROCEDURE
PRUPDATEBOOKQTY
PROCEDURE
PRUPDATEBRANCHSTOCK
PROCEDURE
If you want to find information about objects created by the DBA or any other user, you
query the DBA_OBJECTS or ALL_OBJECTS. Both the views include the OWNER
column in addition to the ones mentioned in the USER_OBJECTS view.
USER_SOURCE View
To view the code of procedures or functions, you can query the USER_SOURCE data
dictionary view. The columns that this data dictionary contains are:
TYPE stores the type of object, that is, whether it is a function, procedure,
package or package body.
178
SQL Star International Ltd.
Compilation errors can be detected using either USER_ERRORS data dictionary view or
iSQL*Plus command SHOW ERRORS.
To be able to see the errors that you may get when compiling your object, query the
USER_ERRORS data dictionary view. You can view the error text message. The
columns that are included in this data dictionary are:
LINE stores the line number for the source code at which the error occurs.
POSITION holds information about the position in the line at which the error
occurs.
You can view the structure of USER_ERRORS using the DESCRIBE command as
follows:
DESC USER_ERRORS
This command displays the following output:
Name
Null?
Type
-----
------
------
NAME
NOT NULL
VARCHAR2(30)
TYPE
VARCHAR2(12)
179
SQL Star International Ltd.
SEQUENCE
LINE
NOT NULL
NUMBER
The SHOW ERRORS command executed along with the name of the procedure displays
the line and column numbers of the errors along with the text of the error message. If you
execute the command without parameters, you get the error data of the last object that
you compiled.
The syntax to use this command is
SHOW ERRORS [FUNCTION | PROCEDURE | PACKAGE | PACKAGE
BODY | TRIGGER |
VIEW] [schema.] name]
For example, executing the following code would result in errors:
CREATE OR REPLACE PROCEDURE prErr
(MemID Member.cMemberID%TYPE)
IS
MemDt DATE;
BEGIN
SELECT dMembershipDt
INTO MemDt
FROM Member;
END;
/
Warning: Procedure created with compilation errors.
The errors could be rectified only when you know what the errors are. Hence, you could
view the compilation errors using SHOW ERRORS as follows:
SHOW ERRORS
Errors for PROCEDURE PRERR:
LINE/COL
ERROR
--------
-------------------------
7/6
one
of the following:
180
SQL Star International Ltd.
The above error could also be viewed using USER_ERRORS data dictionary view as
follows:
SELECT LINE||-||POSITION LINE, TEXT
FROM USER_ERRORS
WHERE NAME=PRERR;
Displays:
LINE
TEXT
-------
-------
7-6
7-7
<a
identifier>
<an
double-quoted
exponent
delimited-
(**)>
as
from
into || bulk
DESCRIBE prIncCopies
PROCEDURE princcopies
Argument Name
-------------
Type
------
VBOOKID
In/Out Default?
-------------
CHAR(13)
IN
181
VCOPIES
NUMBER(2)
IN
(Membership
denied
Invalid
age);
ELSE
DBMS_OUTPUT.PUT_LINE (Membership denied Valid age);
END IF;
END;
/
Above Example takes an input Age from the user. To check where the control is
passed based on condition, DBMS_OUTPUT package is used.
182
SQL Star International Ltd.
Summary
In this chapter, you have learnt that:
A system privilege is given to a user by the DBA to create any object.
An object privilege is the privilege given by a user or a owner to perform an
operation on a particular object of a schema.
To create a subprogram, one must have a CREATE PROCEDURE system
privilege.
To modify (i.e., ALTER, DROP or EXECUTE) a subprogram that belongs to other
schemas, ANY keyword is used with the privilege.
E.g: GRANT ALTER ANY PROCEDURE TO user1;
By default the procedures are invoked by owners right. This is known as Definers
Right. To invoke the procedure by invokers right, specify AUTHID CURRENT_USER
in the subprogram.
Data Dictionary such as USER_OBJECT, USER_SOURCE, USER_ERRORS are used
to give the information regarding created database object, code of the
subprograms, and any uncompiled errors respectively.
SQL*PLUS command, SHOW ERROR is used to display uncompiled errors
immediately and DESCRIBE procedure_name shows the structure of the
procedure.
183
SQL Star International Ltd.
Lab Exercises
1.
Suppose you have lost the code for the prInsJobs procedure and the fnDept
options ON|OFF
SELECT
SET
2.
3.
4.
184
SQL Star International Ltd.
Chapter 9
Creating Packages
What are Packages?
Package Components
Package Constructs
Invoking Package Constructs
Referencing Variables
Persistent State of Variables and Cursors
Removing Packages
Viewing Packages in Data dictionary
Guidelines for Creating Packages
185
SQL Star International Ltd.
Objectives
At the end of this chapter, you will be able to:
Identify package components
Use packages
186
SQL Star International Ltd.
Introducing Packages
As a child you must have played with games like Building Blocks or Lego. These
games taught you how to be innovative and creative and build many structures with
the same set of blocks and pieces. The creator of these blocks built pieces that could
be used in many ways. Imagine if programming were made as easy. Blocks or code
would be created and kept and all you had to do was put the codes together. Well it
is not as easy as building blocks but certainly close.
Easy application design: You can create and compile the specifications for a
package without doing the same for its body. The stored programs that
reference the package can be created and compiled later as and when
required till you have finally decided to complete your application.
Added functionality: The public variables and cursors that are packaged can
be used by other applications that are executing in that session and
environment.
187
SQL Star International Ltd.
Package Components
You now know, why you might require packages in your applications. You got to put
together the Lego pieces to make your structure. To create a package you should
know the structure of a package and its components.
Package specification
Package body
Package Specifications
Package specification is like the interface to an application. It declares the variables,
cursors, user-defined datatypes, functions and procedures that are part of the
package. A package specification is more like the list you find inside a Lego box,
telling you the number of Lego pieces available according to color and shape.
Package Body
Package body fully defines the functions and procedures that are declared in the
specification.
One point to remember is that the package functions and procedures are limited to
the scope of the package. However, they can be called from outside the package by
prefixing them with the package name.
Package Constructs
You know what are the contents of a package. But what is their functional scope? For
instance, not all the Lego pieces can be used everywhere in all structures that you
build. Similarly you need to know which subprograms of a package you can use and
where. Care must be taken to ensure that not all packaged subprograms are used by
all other applications. To ensure this you need to segregate the constructs you
create.
The constructs used in a package could be either Private or Public.
Look at the diagram shown below.
188
SQL Star International Ltd.
Public Constructs
A public construct is one that is declared in the package specification and described
in the package body. For instance, in the above diagram the procedure Public(), is
public and can be used by any database user. A user or application from any Oracle
server environment can call it. Public constructs include:
Public variables are also called global variables as they can be used by all other
applications in the session and thus have a global scope. For instance, vPublic (as
shown in the diagram) is a global variable.
Private Constructs
Private constructs are those that are included in the package body but not declared
in the package specifications. Only modules within the package can call them.
Private variables
Local variables
Private and local variables are not visible to external users. For instance, vPrivate
(as shown in the diagram) is a private variable and vLocal is a local variable.
The difference in private and local variables is that, the scope of private variables is
limited to the package, but the scope of a local variable is limited to the procedure it
is declared in. Other modules in the same package cannot access these variables.
189
SQL Star International Ltd.
Private procedures and functions are those that are described and defined in the
package body. They can only be called from other modules in the same package. In
the diagram, Private() is a private procedure.
Using Packages
Finally you get to use the Lego pieces and build your structures. Using packages
include creating the packages, calling or invoking them and referencing the
variables.
Developing a package has the following three basic steps:
1. Create the package in iSQL*Plus environment
2. Write the code in the editor chosen
3. Compile the code
190
SQL Star International Ltd.
To help the library improve the functionality of the modules that you have already
created, you can design a package specification for them. The procedure to find out
the details of members and the function to find out the fine that they may have to
pay can be combined together as both are related to the library members.
CREATE OR REPLACE PACKAGE pkMemberInfo
IS
PROCEDURE prMemberDetails
(MemberID IN Member.cMemberID%TYPE,
FirstName OUT Member.cFirstName%TYPE,
LastName OUT Member.cLastName%TYPE,
191
SQL Star International Ltd.
Let us create the package body for the package specification compiled earlier to find
out information pertaining to members and the calculation of their fines.
CREATE OR REPLACE PACKAGE BODY pkMemberInfo
IS
192
SQL Star International Ltd.
PROCEDURE prMemberDetails
(MemberID IN Member.cMemberID%TYPE,
FirstName OUT Member.cFirstName%TYPE,
LastName OUT Member.cLastName%TYPE,
Address OUT Member.vAddress%TYPE)
IS
BEGIN
SELECT cFirstName,cLastName,vAddress
INTO FirstName,LastName,Address
FROM Member
WHERE cMemberID=MemberID;
DBMS_OUTPUT.PUT_LINE(MemberID||:||FirstName||
||LastName||||Address);
END prMemberDetails;
FUNCTION fnFineCal
(MemberID Member.cMemberID%TYPE)
RETURN NUMBER
IS
vReturnDt DATE;
vFine NUMBER;
vActReturnDt DATE;
BEGIN
SELECT dReturnDt,dActualReturnDt
INTO vReturnDt,vActReturnDt
FROM Transaction
WHERE cMemberID=MemberID;
IF vReturnDt >= vActReturnDt THEN
vFine:=0;
ELSE
193
SQL Star International Ltd.
vFine:=(vActReturnDt-vReturnDt)*1.50;
END IF;
DBMS_OUTPUT.PUT_LINE (The member has to
pay a fine of Rs. ||vFine);
RETURN(vFine);
END fnFineCal;
END pkMemberInfo;
/
Once the package is created the body needs to be compiled. Packages can be
compiled either on the client-side or the server-side.
On compiling the package body, you get the message confirming the successful
compilation and creation of the package body:
Package body created.
You must ensure that the package specification is compiled before the
package body.
The package body should contain the program codes for all units defined in
the specifications else it will not get compiled.
One good point about the package creation and compilation is that, the components
in the package do not have to be in the same order as they have been defined. In
case you have made changes in the code of any of the programs then you need to
only recompile the body. In case the change is in the number or type of the
parameters, then the specification also needs to be recompiled.
You can also create a package without a body. The only time when a package body is
unnecessary is when a specification declares only user-defined types, constants,
variables and user-defined exceptions.
For example, consider the following package specification:
CREATE OR REPLACE PACKAGE pkForConversions
IS
kilo_to_mile CONSTANT NUMBER := 0.6214;
194
SQL Star International Ltd.
mile_to_kilo CONSTANT
NUMBER := 1.6093;
END packForConversions;
/
Package created.
In the code, two identifiers kilo_to_mile and mile_to_kilo declared as constants
specify rates for converting kilometer to mile and mile to kilometer respectively.
A package body is not required for this package specification because the constructs
of the specification require no implementation details. You can directly execute the
package as follows:
EXECUTE DBMS_OUTPUT.PUT_LINE ( 15 kilometers = || 15*
pkForConversions.kilo_to_mile|| miles);
To invoke the package constructs you need to prefix the name of the construct with the
name of the package. The syntax for it is:
<package_name>.<procedure or function_name>
In case you are calling a construct from within the package then you need not prefix
the package name.
To invoke a package construct from the iSQL*Plus environment use the EXECUTE
command followed by the package and procedure name.
When required you can also specify the schema to which the package belongs.
To invoke the function fnFineCal of the pkMemberInfo for calculating fine payable
by the members, you must first declare a variable to hold the value returned by the
function:
SQL>VARIABLE Fine NUMBER
After having declared a variable Fine, you can invoke the fnFineCal function using
the EXECUTE command.
SQL>EXECUTE :Fine :=pkMemberInfo.fnFineCal(BJH029405);
This displays the following result:
195
SQL Star International Ltd.
Green
Referencing Variables
Apart from invoking and calling the package procedures and functions, you can also
refer to the variables in a package. Referencing to package variables can be done
from within the package as well as from outside. When referencing from within the
package you need not prefix the package name. If you want the variables to be
referenced from outside the package from another stand-alone procedure, then you
have to ensure that you declare them publicly when creating the package.
For example, create a package with function to validate the age of a new member as
shown below.
196
SQL Star International Ltd.
SELECT MIN(nAge)
INTO MinAge
FROM Member;
IF MemAge<MinAge THEN
DBMS_OUTPUT.PUT_LINE (The member is too young);
RETURN(FALSE);
ELSE
DBMS_OUTPUT.PUT_LINE(This is the age of new
member);
RETURN(TRUE);
END IF;
END fnValidateAge;
PROCEDURE prCheckAge
(MemberAge Member.nAge%TYPE)
IS
BEGIN
IF fnValidateAge(MemberAge)THEN
gMemberAge:=MemberAge;
ELSE
RAISE_APPLICATION_ERROR(-20850, Invalid Member
Age);
END IF;
END prCheckAge;
END pkCheckAge;
/
On compiling the above package, you get the following confirmation message:
Package created.
Package body created.
197
SQL Star International Ltd.
This package contains a global variable, a public procedure and a private function.
The value passed as the global variable can be changed by directly referencing the
variable with the package name. It can also be overridden by the procedure within
the package.
To assign a value to the global variable directly, issue the following EXECUTE
command.
EXECUTE pkCheckAge.gMemberAge:= 6;
PL/SQL procedure successfully completed.
This initializes the global variable to 6. In case you want to check for another
members age, execute the procedure as follows:
EXECUTE pkCheckAge.prCheckAge(4);
Executing the above procedure results in the display of the following message:
The member is too young
The value when the variable is released when you disconnect from the session
In the pkCheckAge package, you can track the value of the global variable
gMemberAge and the procedure variable MemberAge at every stage. This is
198
SQL Star International Ltd.
possible by inserting display messages after every assignment and calculation, in the
function and procedure.
199
SQL Star International Ltd.
LOOP
FETCH curCategoryBooks INTO BookName;
DBMS_OUTPUT.PUT_LINE(BookName);
EXIT WHEN curCategoryBooks%ROWCOUNT>=1;
END LOOP;
END prFirstSet;
PROCEDURE prSecondSet
IS
BEGIN
LOOP
FETCH curCategoryBooks INTO BookName;
DBMS_OUTPUT.PUT_LINE(BookName);
EXIT WHEN curCategoryBooks%ROWCOUNT>=3;
END LOOP;
CLOSE curCategoryBooks;
END prSecondSet;
END pkCatgBookDisplay;
/
On compiling the above package code, you get the following message:
Enter value for catgname: Fiction
Package created.
Package body created.
Execution of this package causes the cursor to fetch all the rows whose display is
controlled by the two procedures. The first fiction book is displayed by prFirstSet.
The cursor is not closed and hence the control moves onto the second procedure. At
this point the cursor status is at the 2nd row. The second procedure prSecondSet
hence displays the names of the remaining books starting from the 2nd book.
EXECUTE pkCatgBookDisplay.prFirstSet;
Triumph Of Katie Byrne
PL/SQL procedure successfully completed.
EXECUTE pkCatgBookDisplay.prSecondSet;
Wings Of The Storm
200
SQL Star International Ltd.
Woman Of Substance
Three points to remember about the state of package cursors and variables are that:
They persist for transactions in a session. The cursor and variable states will
persist for all transactions in a session until you close the cursor or end the
package.
They change from one session to another for a single user. If a certain user
logs out of one session and starts another session invoking the same
package, the cursor and variable states is set to its initial value. In other
words, the cursor and variable states do not persist across sessions for the
same user.
They change from one user to another. If there are different users in different
sessions accessing the same package, the state of the cursors and variables
are all set to their initial values for both users.
Removing Packages
Package like any other database object can be removed when it is no longer
required. You must be sure that the database users no longer require the procedures
and functions in the package.
You can drop the entire package, that is, specification and body, by issuing the
DROP PACKAGE command, followed by the package name. The syntax is:
DROP PACKAGE <package_name>;
To drop the package pkCatgBookDisplay issue the following command:
DROP PACKAGE pkCatgBookDisplay;
Package dropped.
If you only want to drop the package body then use the following syntax:
DROP PACKAGE BODY <package_name> ;
To drop the pkCatgBookDisplay issue the following command:
DROP PACKAGE BODY pkCatgBookDisplay;
201
SQL Star International Ltd.
Summary
In this chapter, you have learnt that:
Packages are schema objects, which enclose collection of related procedures and
functions, cursors, exception and variables.
Package are made of two separate components sharing the same name. They
are Package specification and Package body.
The constructs within the package are invoked by prefixing package name with
the construct name.
The cursor and variable states will persist for all transactions in a session until
you close the cursor or end the package.
202
SQL Star International Ltd.
Lab Exercises
1.
Create a package specification and body called pkEmp that contains the
following:
Last name
First name
Email ID
203
SQL Star International Ltd.
2.
Create a package specification and body called pkCheck that contains the
following two procedures (public):
204
SQL Star International Ltd.
Chapter 10
205
SQL Star International Ltd.
Objectives
206
SQL Star International Ltd.
When you execute overloaded programs, the Oracle compiler searches for the
declarations that match the subprograms. The search takes place first in the current
scope and then if required moves on to successive enclosing scopes. If the compiler
finds one or more subprogram signatures in which the subprogram name matches
that of the called program, then it compares the number, data type and order
between the actual and formal parameter.
The user DBA creates two procedures to add rows into the PublisherDetails table
and calls both the procedures prAddPublisher. He places both procedures in a
package. One procedure accepts the values of cPublisherID and cPublisherName.
The other procedure accepts cPublisherID, cPublisherName and
cPublisherAddress.
CREATE OR REPLACE PACKAGE pkLoad
207
SQL Star International Ltd.
IS
PROCEDURE prAddPublisher
(PublisherID IN PublisherDetails.cPublisherID%TYPE,
PublisherName IN PublisherDetails.cPublisherName%TYPE);
PROCEDURE prAddPublisher
(PublisherID IN PublisherDetails.cPublisherID%TYPE,
PublisherName IN PublisherDetails.cPublisherName%TYPE,
Address IN PublisherDetails.vPublisherAddress%TYPE);
END pkload;
/
CREATE OR REPLACE PACKAGE BODY pkLoad
IS
PROCEDURE prAddPublisher
(PublisherID IN PublisherDetails.cPublisherID%TYPE,
PublisherName IN PublisherDetails.cPublisherName%TYPE)
IS
BEGIN
INSERT
INTO
PublisherDetails(cPublisherID,cPublisherName)
VALUES(PublisherID,PublisherName);
END prAddPublisher;
PROCEDURE prAddPublisher
(PublisherID IN PublisherDetails.cPublisherID%TYPE,
PublisherName IN PublisherDetails.cPublisherName%TYPE,
Address IN PublisherDetails.vPublisherAddress%TYPE)
IS
BEGIN
INSERT INTO PublisherDetails(cPublisherID,
cPublisherName,vPublisherAddress)
208
SQL Star International Ltd.
VALUES(PublisherID,PublisherName,Address);
END prAddPublisher;
END pkLoad;
/
To determine which of these procedures will execute, issue the following EXECUTE
statements:
EXECUTE pkLoad.prAddPublisher(RQ0865,Raj Quartet);
PL/SQL procedure successfully completed.
EXECUTE
pkLoad.prAddPublisher(BW0780,Booksware,New
Orleans);
PL/SQL procedure successfully completed.
To verify whether the values have been inserted into the PublisherDetails table, issue a
SELECT statement as shown below.
SELECT *
FROM PublisherDetails
WHERE cPublisherID IN (RQ0865,BW0780');
CPUBLISHERNAME
VPUBLISHERADDRESS
-------
---------------
-----------------
BW0780
Booksware
RQ0865
Raj Quartet
New Orleans
Forward Declarations
PL/SQL does not support forward references. That is, an identifier or a subprogram must
be declared before calling it.
Look at the example given below.
209
SQL Star International Ltd.
FUNCTION fnValidateAge
(MemAge Member.nAge%TYPE)
. . . . . . . . . . . . . . . . . .
. . . . . . . . . . . . . . . . . .
END fnValidateAge;
END pkCheckAge;
In the example, you cannot reference the function fnValidateAge because it has not
yet been declared. Reversing the order of the two programs could solve such an
illegal reference.
But such a solution does not always work, especially if the programs are calling one
another, or you want the programs to be defined in some order, such as in
alphabetical order.
To solve this kind of problem you can implement forward declarations. Forward
declarations require the subprograms specification declarations to be ended using a
semicolon. For example, in case you want to define your programs in an alphabetical
order then mention only the name and arguments of the private program before the
public one and define the private program later according to its alphabetic
occurrence.
When compiling this package the server will read the public procedure, encounter the
name of the private procedure, and search for the same in the package body above
the public procedure. When it finds the procedure name, it calls the actual procedure
body that is defined later in the package body.
210
SQL Star International Ltd.
The best example to illustrate forward declaration is the pkCheckAge package created
earlier. The prCheckAge procedure within the package references the private function
fnValidateAge only after the function has been declared.
CREATE OR REPLACE PACKAGE BODY pkCheckAge
IS
FUNCTION fnValidateAge
(MemAge Member.nAge%TYPE); -- Forward declaration
PROCEDURE prCheckAge
(MemberAge Member.nAge%TYPE)
IS
BEGIN
IF fnValidateAge(MemberAge)
. . . . . . . . . . . . . . . .
END prCheckAge;
FUNCTION fnValidateAge
(MemAge Member.nAge%TYPE)
. . . . . . . . . . . . . .
BEGIN
. . . . . . . . . . . . . . .
END fnValidateAge;
211
SQL Star International Ltd.
END pkCheckAge;
/
Automatic initialization of
FROM Transaction
vFine variable
212
SQL Star International Ltd.
In the example, current value for vFine is set with the value of nfine from the
Transaction table the first time the package pkFine is referenced.
pkincrfine
IS
FUNCTION fnincrfine (vfine IN NUMBER)
RETURN NUMBER
IS
BEGIN
RETURN vfine*.01;
END fnincrfine;
END pkincrfine;
/
Package created.
Package Body created.
In order to access the above package following statement has to be executed.
SELECT
Transaction;
cmemberid,
nfine,
pkincrfine.fnincrfine(nfine)
FROM
213
SQL Star International Ltd.
214
SQL Star International Ltd.
2. Input File ignores semantic errors. However .plb file containing the wrapped code
reports the error once executed. Alternatively, one can compile a package body and
then wrap to avoid this.
3. Changes made to the original source have to be wrapped again.
Managing Packages
Managing packages involve the privileges needed to access them, tracking their
execution, and ensuring that they do not violate data integrity rules already defined.
To be able to manage packages you can:
Access the source codes of the procedures using the USER-SOURCE
data dictionary view.
View their parameters using the DESCRIBE command.
Compile time errors can be tracked and viewed with the USER_ERRORS data
dictionary view and the command SHOW ERRORS.
Some database objects are dependent on other database objects. The objects that
require data or support from other database objects are called dependant objects
and the objects that are referred to are called referenced objects.
Dependencies
When managing packages and other independent subprograms you need to include
all dependency issues. Changing the structure of a referenced object may cause a
dependent to stop functioning.
The Oracle server manages all dependencies, and to do so, it takes the help of the
Status column of the data dictionary view USER_OBJECTS. The status of an object
at any time can either be VALID or INVALID.
The difference in the two is that a VALID object has been compiled and is ready for
use, whereas an INVALID object has to be compiled before it is used.
215
SQL Star International Ltd.
NULL?
TYPE
---------
-----------
-------------
NAME
NOT NULL
VARCHAR2(30)
TYPE
VARCHAR2(12)
REFERENCED_OWNER
VARCHAR2(30)
REFERENCED_NAME
VARCHAR2(64)
REFERENCED_TYPE
VARCHAR2(12)
REFERENCED_LINK_NAME
VARCHAR2(128)
SCHEMAID
NUMBER
DEPENDENCY_TYPE
VARCHAR2(4)
The data that you can query from this view are:
to
reference
the
object
from
the
216
SQL Star International Ltd.
UTLDTREE
Another method of viewing dependencies is to query the DEPTREE view, which lists
the dependency information that is stored in an underlying table called
DEPTREE_TEMPTAB, and the IDEPTREE view, which lists dependency information
such that dependent objects are shown below the objects they are dependent on,
and they are indented.
To query the views, you need to first run the Oracle provided script file UTLDTREE
(found in the rdbms/admin subdirectory under the Oracle software home directory).
You need to then execute a procedure called DEPTREE_FILL, which populates the
underlying table for your use.
The syntax to execute the procedure is:
EXECUTE DEPTREE_FILL( <object_type>, <object_owner>,
<object_name>);
The above syntax is used to execute the procedure to populate the table
DEPTREE_TEMPTAB with information of a referenced object. The three parameters
passed are the type of the object being referred, the owner of the object and the
name of the object. For example,
EXECUTE DEPTREE_FILL(TABLE,SCOTT,TRANSACTION);
PL/SQL procedure successfully completed.
Query the DEPTREE view as shown below.
SELECT NESTED_LEVEL, TYPE, NAME
FROM DEPTREE
ORDER BY SEQ#;
Displays:
NESTED_LEVEL
TYPE
-------------- ----
NAME
------
TABLE
TRANSACTION
VIEW
TI
PACKAGE BODY
PKMEMBERINFO
217
SQL Star International Ltd.
referred object, the dependent object becomes invalid. The next time the invalid
object is called, it is recompiled by the server automatically and hence, refers to the
modified referenced object.
If you change the name of the referenced object then it will invalidate your
dependent object. Even if you have two different types of objects with the same
name, check to see which object will your dependent object refer to.
Remote Dependencies
Dependencies are said to be remote when the database objects are residing on
different nodes in the database. Dependencies among remote objects are not
handled automatically, but the server handles local-procedure-to-remote-procedure
dependencies. If there is a change in the structure of a referenced object, then all its
dependent objects will be invalid. They are not automatically recompiled when called
the next time.
Points to remember with regards to remote dependencies are:
There are some parameters to set the remote dependency mode. This can be done
at three levels as listed below.
REMOTE_DEPENDENCIES_MODE=value
218
SQL Star International Ltd.
The remote dependency mechanism is not the same as the local one. When a
remote recompiled procedure is invoked, you will get an error and the local
program is invalidated. The next time it is invoked, it gets implicitly
recompiled.
When a procedure is compiled, the time stamps of both, the local procedure
and that of the remote procedure it calls are recorded in the p-code of the
local procedure. When you invoke the local procedure it displays an error
message saying that the timestamp of the remote procedure has changed. To
rectify this, re-invoke the local procedure.
Procedure Dependencies
Dependency of database objects essentially comes into focus when you need to recompile
the objects due to some alteration that you may have done on the blocks.
This requires you to recompile your blocks. There are some issues that you should know
while programming and developing and later implementing procedures and functions.
Recompiling PL/SQL Blocks
Blocks can be explicitly or implicitly recompiled. When recompiling an object, the server
will first recompile any invalid object on which the former is dependent.
219
SQL Star International Ltd.
Triggers are enabled or validated by default and when invalidated, it does not
fire when the triggering statement is executed.
Columns of a referenced view changes and the required columns are not part
of the new view
Datatype of the referenced column has not changed and no new column is
NOT NULL
If the referenced object is a private table that is dropped and a public table
exists. When the private table is dropped, the dependent object becomes
invalid. But if a public table is created with the same name and structure,
then the object refers to the public table and is recompiled.
Package Dependencies
Package dependency includes the package body being dependent on the package
specifications. When you create and compile a package, the details of both the
specification and the body are found in the USER_OBJECTS view.
Some points to remember about compilation are:
The package body compilation will remain successful as long as the package
specification does not change. In case there is some alteration made to a
package function and the package body is recompiled, the recompilation is
successful. This procedure independence is very useful, as you need not
recompile all the dependent procedures and functions, only the altered
procedure needs to be recompiled.
220
SQL Star International Ltd.
Cyclic dependencies are also not a problem because, when one package is
getting compiled it will check the specification of the referenced package for
the referenced procedure.
221
SQL Star International Ltd.
Summary
In this chapter, you have learnt that:
Packages are said to be overloaded when they have functions and procedures
with same name, but differ in the argument datatype and number.
Forward Declaration in package body helps compiling of those subprograms,
which are invoked first and later defined. By doing so, an ordered subprogram
can be maintained in the package body.
ONE-TIME ONLY procedure helps to dynamically initialize the package variable.
PL/SQL wrapper utility is used to convert the source code into unreadable
format for security reasons.
Oracle server manages dependencies by taking the help of the Status column
of the data dictionary view USER_OBJECTS. The status of an object at any
time can either be VALID or INVALID.
222
SQL Star International Ltd.
Lab Exercises
1.
Create a package called pkOverLoadDisplay. The package contains two
functions having the same name fnDisplay.
The first function accepts a date (in DD-MON-YY format) and displays it in
fmDdspth, Month, YYYY format.
Convert the following PL/SQL code into portable object code so that you can
223
SQL Star International Ltd.
Chapter 11
224
SQL Star International Ltd.
Objective
At the end of this chapter, you will be able to:
Use Oracle supplied packages
225
SQL Star International Ltd.
DBMS_OUTPUT displays any messages from triggers and other PL/SQL blocks.
Some of these packages are used often and you need to know why and how they are used.
You will learn about the following packages in detail.
DBMS_SQL
DBMS_OUTPUT
226
SQL Star International Ltd.
HTP
UTL_FILE
DBMS_SCHEDULER
DBMS_METADATA
DBMS_WARNING
We will also discuss about EXECUTE IMMEDIATE statement which works similar to
DBMS_SQL.
DBMS_SQL
This package enables you to write PL/SQL blocks using dynamic SQL. Dynamic SQL
comprises statements that are not coded into the block but are built by the block during
runtime. That is, SQL statements can be created dynamically at runtime using variables.
For example, you can perform DML operations on tables accepting the name of the table
at runtime. You can also issue DDL statements using this package. For example you can
drop a table from within a procedure.
To use SQL statements in PL/SQL you need to use cursors. The regular flow of a
program using SQL statements and cursors is as follows:
1. Open the cursor to process the SQL statement. The cursor returns the cursor ID
number.
2. The SQL statements are parsed to check for syntax and privileges. A private SQL
area is allotted for the statement.
3. Each data input at runtime must have a bind variable to supply the values to a
placeholder. Use BIND_VARIABLE for this purpose.
4. Use DEFINE_COLUMN to specify the variables that will store the values from
the SELECT statement.
5. The SQL statements need to be executed.
6. The rows that satisfy the condition will be fetched with the FETCH_ROWS
statement.
7. Verify the values of the columns or the variables fetched by using the
COLUMN_VALUE or the VARIABLE_VALUE statements respectively.
8. Close the cursor with the CLOSE_CURSOR statement to de-allocate the memory
space.
SQL statements need not be included in PL/SQL blocks. They can be stored as
strings and passed to the program at runtime. This allows the programmer to
write generic modules that can be reused.
227
SQL Star International Ltd.
The package allows parsing, hence DDL statements can be parsed when used
in PL/SQL.
These operations are performed under the current user and hence if there is
an anonymous block calling the subprogram then the privileges of the current
user prevails. If the call is from a stored procedure then the privileges of its
owner are applied.
INTEGER;
BEGIN
vCursorName := DBMS_SQL.OPEN_CURSOR;
-- opens a cursor and assigns an ID
DBMS_SQL.PARSE (vCursorName, DELETE
FROM || vtableName, DBMS_SQL.NATIVE);
-- parses the DML/DDL statements
vRowsDeleted := DBMS_SQL.EXECUTE
(vCursorName); -- executes the SQL statements and returns a
message of how many rows were processed
DBMS_SQL.CLOSE_CURSOR (vCursorName); -- closes the
specified cursor
228
SQL Star International Ltd.
END;
/
For better performance of native dynamic SQL, use the EXECUTE IMMEDIATE
statement. Its syntax is as follows:
EXECUTE IMMEDIATE dynamicSQL_string
[INTO {define_variable [, define_variable] | record}]
[USING [IN | OUT | IN OUT] bind_argument,
[IN | OUT | IN OUT] bind_argument] ];
[{RETURNING | RETURN} INTO bind_argument [,
bind_argument]...];
Where,
dynamicSQL_string is a string expression representing a dynamic SQL statement (no
terminator) or a PL/SQL block (terminator)
define_variable is a variable to store the selected column value
record is a %ROWTYPE or user-defined record to store a selected row
input bind_argument : is the expression whose value is passed to the dynamic SQL
statement
output bind_argument : is a variable in which values returned by the dynamic SQL
statement is stored.
229
SQL Star International Ltd.
IS
BEGIN
EXECUTE IMMEDIATE DELETE FROM ||vTableName;
vRowsDeleted := SQL%ROWCOUNT;
END;
/
In the code, the EXECUTE IMMEDIATE statement parses and immediately executes the
dynamic SQL statement.
230
SQL Star International Ltd.
IS
stmt VARCHAR2(100):=ALTER
||COMPILE;
||plsql_type||
||
name
BEGIN
IF options IS NOT NULL THEN
stmt := stmt || ||options;
END IF;
EXECUTE IMMEDIATE stmt;
END;
/
To make programs flexible. For instance, you may want to postpone your choice
of schema objects until runtime, or you may want the program to build different search
conditions for the WHERE clause of SQL statement.
DBMS_OUTPUT Package
We have been using this package for displaying the output. Let us have an indept
knowledge about this package now.
The DBMS_OUTPUT package is made up of the following subprograms.
PUT, NEW_LINE, PUT_LINE, GET_LINE AND GET_LINES.
231
SQL Star International Ltd.
vFine NUMBER;
vActReturnDt DATE;
BEGIN
SELECT dReturnDt,dActualReturnDt
INTO vReturnDt,vActReturnDt
FROM Transaction
WHERE cMemberID=MemberID;
IF vReturnDt=vActReturnDt OR vReturnDt>vActReturnDt THEN
vFine:=0;
ELSE
vFine:=(vActReturnDt-vReturnDt)*1.50;
END IF;
DBMS_OUTPUT.PUT(vFine);
DBMS_OUTPUT.PUT_LINE (The member has to pay a fine of Rs.
||vFine);
RETURN(vFine);
END;
/
Function created.
On executing the function,
EXECUTE :FN:=fnFineCal(BJH029405);
You get the following result:
0 The member has to pay a fine of Rs. 0
In the code, DBMS_OUTPUT.PUT (vFine) puts the fine amount into the buffer. This
amount is then displayed along with the other text from the buffer by the use of
DBMS_OUTPUT.PUT_LINE().
NEW_LINE ()
This procedure puts information into the output buffer with the carriage return line
feed. This character is represented as CHR(10) in an SQL SELECT statement.
This procedure does not take any parameters.
DBMS_OUTPUT.PUT_LINE() is a combination of put and New_line.
ENABLE () and DISABLE ()
232
SQL Star International Ltd.
The ENABLE () procedure allows the code to access the buffer so that the output of
the code can be written within an iSQL*Plus session. One parameter value that you
need to provide is the size of the buffer to be allocated. On the other hand if you
dont want the code to be written to the buffer, use the procedure DISABLE (). This
does not accept any parameters.
A point to be remembered is that the buffer size that you pass should be large
enough to hold all the data coming through. In case there is data over and above the
size you passed then that data is discarded automatically. The maximum size of the
buffer is 1,0000,000 bytes. Default size is 2000.
Earlier, programmer for the library database, created a function to calculate the fine
if any, to be paid by a member when he returns a book he borrowed. You already
know how to create a function. All you need to do is include the procedure ENABLE ()
in the body of the code at the beginning before the transaction starts. Include
DISABLE () at the end of all the transaction statements.
GET_LINE ()
It retrieves data from the output buffer and returns it into the variables passed as its
parameters. It accepts all data, converts them into VARCHAR2 data and displays it.
Two out parameters are passed with this procedure. One that displays each line of
information from the buffer and the other is a counter or status parameter that
checks if the procedure was successful and if there are any more lines for display in
the buffer. You need to declare variables for the parameters to use this procedure.
However, to display the values passed into the parameter variables, you need to use
PUT_LINE () procedure.
For example, to display the content put in the buffer when compiling fnFineCal, you
use the GET_LINE () procedure as follows:
CREATE OR REPLACE FUNCTION fnFineCal
(MemberID Member.cMemberID%TYPE)
RETURN NUMBER
IS
vReturnDt DATE;
vFine NUMBER;
vActReturnDt DATE;
vLineStatus NUMBER;
BEGIN
SELECT dReturnDt,dActualReturnDt
INTO vReturnDt,vActReturnDt
FROM Transaction
WHERE cMemberID=MemberID;
IF vReturnDt=vActReturnDt OR vReturnDt>vActReturnDt THEN
vFine:=0;
ELSE
vFine:=(vActReturnDt-vReturnDt)*1.50;
233
SQL Star International Ltd.
END IF;
DBMS_OUTPUT.GET_LINE(vFine,vLineStatus);
DBMS_OUTPUT.PUT_LINE (vFine);
DBMS_OUTPUT.PUT_LINE (vLineStatus);
RETURN(vFine);
END;
/
The result obtained on executing the above function is:
EXECUTE :FN:=fnFineCal(DDG019503);
3
1
The above result displays the fine amount to be paid by member having ID
DDG019503 and also whether the GET_LINE () procedure was successful or not.
GET_LINES ()
In case you need to retrieve more than a line from the buffer then the GET_LINE ()
will not serve the purpose. You need to use the GET_LINES () procedure. This
procedure also takes in two parameters. One is a character array called CHARARR,
which stores the lines to be displayed. The other is of NUMBER type that stores the
number of lines to be displayed.
DBMS_DDL
This package provides you with access to some DDL statements. The procedures in this
package include:
You cannot use this package in triggers and procedure calls from Forms Builder or
remote sessions.
234
SQL Star International Ltd.
HTP Package
HTP Package is a web-driven version of DBMS_OUTPUT that helps us to create
well-formed HTML tags. It originates from PL/SQL toolkit which contains packages for
developing web applications.
HTP package is made up of the following procedures.
1. Anchor: Generates an anchor tag.
2. Print: Prints any value passed as a parameter.
3. Wrapper Procedure:
HTML tag.
Let us create a HTML web page using this package in an iSQLPlus interface.
BEGIN
HTP.HTMLOPEN;
HTP.HEADOPEN;
HTP.TITLE(Welcome to New Jersey Library);
HTP.HEADCLOSE;
HTP.BODYOPEN;
HTP.PRINT(Home page for this library under
HTP.BODYCLOSE;
HTP.HTMLCLOSE;
END;
/
SQL> Execute
OWA_UTIL.SHOWPAGE;
235
SQL Star International Ltd.
</BODY>
</HTML>
To specify the special features, the text must be enclosed in the HTML tag pair. With
the help of Htp.print the HTML tag can be generated thereafter.
For example
HTP.PRINT(<I> SqlStar <I>);
DBMS_SCHEDULER
Job Scheduling has been enhanced in Oracle10g through this package. It aims at
creating, scheduling and managing the jobs in a modular fashion thereby simplifying
the task. Collectively the subprograms within the package are called Scheduler,
which can be invoked from any PL/SQL program.
CREATING A JOB
DBMS_SCHEDULER.CREATE_JOB procedure is used to create job, which is in disable
state initially. A job becomes active and scheduled when it is explicitly enabled.
To create a job:
You need to have CREATE JOB privilege.
You need to specify a job name prefixed by schema name. Schema name is
236
SQL Star International Ltd.
optional.
237
SQL Star International Ltd.
END;
/
The above code creates a job Job_1 which inserts values into a table using sequence
SEQ every minute.
BEGIN
DBMS_SCHEDULER.CREATE_PROGRAM(
PROGRAM_NAME=>SEQ_GEN,
PROGRAM_TYPE=>PLSQL_BLOCK,
PROGRAM_ACTION=>BEGIN INSERT INTO Countertab VALUES
(SEQ.NEXTVAL);END;);
END;
/
The above code creates a program SEQ_GEN which inserts values into Countertab
table.
2. Creating Schedule Components.
BEGIN
DBMS_SCHEDULER.CREATE_SCHEDULE(SCHED_NAME,
START_DATE=>SYSTIMESTAMP,
REPEAT_INTERVAL=>FREQ=DAILY,
238
SQL Star International Ltd.
END_DATE=>SYSTIMESTAMP+15);
END;
/
The above code creates a schedule SCHED_NAME which runs for 15 days only.
Following string expressions can be specified for REPEAT_INTERVAL:
REPEAT_INTERVAL=>FREQ=HOURLY;INTERVAL=2'
hours
239
SQL Star International Ltd.
UTL_FILE Package
The UTL_FILE package extends I/O to text files within PL/SQL. This package
implements:
Client-side security using normal operating system file permission checking
Server-side security through restrictions on the directories that can be accessed
The UTL_FILE package provides security for the directories on the server through the
init.ora file, by setting the initialization parameter UTL_FILE_DIR to the accessible
directories desired.
You can use the procedures and functions of the package to:
Open files
Close files
The package also contains seven exceptions to account for possible errors that may rise
during execution.
The file processing involved when using UTL_FILE package is diagrammatically
represented below.
Before you use the UTL_FILE package to read from or write to a text file, ensure
whether the text file is open by using the IS_OPEN function. If the file is not open, open
240
SQL Star International Ltd.
the file using FOPEN function. You can then read from the file or write to the file until
the processing is done. After the file processing is done, close the file using FCLOSE
procedure.
UTL_FILE Procedures, Functions and Exceptions
The table below lists the procedures, functions and exceptions that are specific to the
UTL_FILE package.
241
SQL Star International Ltd.
Where,
location is the operating system specific string, which specifies the area (or directory) in
which the file has to be opened.
filename is the name of the file along with its extension. The path information is not
specified.
open_mode is a string that specifies how a file is to be opened. For instance,
r- read text (use GET_LINE)
w- write text (use PUT, PUT_LINE, NEW_LINE,
PUTF, FFLUSH)
a- append text (use PUT, PUT_LINE, NEW_LINE,
242
SQL Star International Ltd.
PUTF, FFLUSH)
IS_OPEN Syntax
FUNCTION IS_OPEN
(file_handle IN FILE_TYPE)
RETURN BOOLEAN;
DBMS_METADATA
DBMS_METADATA is the PL/SQL package that implements Metadata API(application
programming interface). It allows us to:
Though this package was introduced in Oracle 9i, the actual use was introduced in
Oracle10g in the data pump to load and unload metadata.
Let us extract the information about Member table in SCOTT SCHEMA using
DBMS_METADATA.
243
SQL Star International Ltd.
DOC CLOB;
BEGIN
h:=DBMS_METADATA.OPEN(TABLE);
DBMS_METADATA.SET_FILTER(h, SCHEMA, SCOTT);
DBMS_METADATA.SET_FILTER(H, NAME, MEMBER);
t1:= DBMS_METADATA.ADD_TRANSFORM(h, DDL);
DOC:=DBMS_METADATA.FETCH_CLOB(H);
DBMS_METADATA.CLOSE(H);
RETURN DOC;
END;
/
Browsing APIs
Browsing APIs are designed for casual use within SQL clients such as SQL*Plus.
Using these functions, metadata for objects can be fetched with a single call. Object
type definition can be extracted based on XML version or DDL using the appropriate
functions.
More than one function can also be used for certain objects. For example, GET_XXX
can be used to fetch an index by name and the same index can be fetched by using
GET_DEPENDENT_XXX by specifying the table on which it is defined.
When you invoke the above functions in iSQL*Plus to retrieve complete,
uninterrupted output, SET LONG and SET PAGESIZE commands must be used.
SET LONG 2000000 SET PAGESIZE 300
An example for using DBMS_METADATA:
SELECT DBMS_METADATA.GET_XML(TABLE,MEMBER,SCOTT)
FROM DUAL;
Issue the following statement to fetch the DDL for all the object grants on
SCOTT.MEMBER:
SELECT DBMS_METADATA.GET_DEPENDENT_DDL OBJECT_GRANT,MEMBER,SCOTT)
FROM DUAL;
244
SQL Star International Ltd.
The warnings issued by the Oracle PL/SQL compiler can be selectively enabled and
disabled by using
1)PLSQL_WARNINGS initialization paramerter
2)DBMS_WARNINGS PACKAGE
Warnings can be Categorized as
SEVERE
PERFORMANCE
INFORMATIONAL
ALL
DBMS_WARNING Package:
This package helps us to read and change the setting done by PLSQL_WARNINGS
programitically. It provides useful subprograms to select, modify and delete current
system or session level settings.
A list of subprograms are listed below.
ADD_WARNING_SETTING_CAT(w_category,w_Value,scope): Modifies the
current session or system warning settings of the warning_category previously
supplied.
245
SQL Star International Ltd.
s_scope
For example:DBMS_WARNING.SET_WARNING_SETTING_STRING(ENABLE:ALL,SESSION);
DBMS_WARNING.ADD_WARNING_SETTING_CAT(PERFORMANCE,DISABLE);
GET_CATEGORY: Returns the category name for the given message number.
246
SQL Star International Ltd.
Summary
In this chapter, you have learnt that:
DBMS_SQL: Using this package, one can write codes using DDL and DCL
statements within a block which is normally not allowed.
EXECUTE IMMEDIATE: This statement is also used to execute DML, DDL and
DCL statements dynamically.
DBMS_OUTPUT: This package help to show the output to the users console.
DBMS_DDL: Through this package, one can dynamically compile the objects
and analyze the database objects.
HTP : HTML code can be generated to develop web pages using this package.
DBMS_SCHEDULER: This allows us to schedule the jobs and manage them in a
247
SQL Star International Ltd.
modular fashion.
UTL_FILE: Input-output facilities are given by using this package.
DBMS_METADATA: Database objects metadata can be retrieved as XML code
output or DDL code output using this package.
DBMS_WARNING: This helps to dynamically enable or disable the warning
settings at session level or at system level.
248
SQL Star International Ltd.
Lab Exercises
1.
Create a procedure prDeleteRows that will dynamically delete rows from a
specified table. Use an OUT parameter to display the number of rows deleted as a result
of the execution of dynamic SQL.
To test the procedure, create a table Employee, which is a copy of the Employees table.
Now, execute the prDeleteRows procedure to delete rows from the table created.
2.
Use EXECUTE IMMEDIATE to perform the same dynamic SQL, as done with
DBMS_SQL package in question 4. [Note: Remember to drop the table Employee and
re-create it for the purpose of this question]
3.
Create a procedure prAnalyzeDBObjects, which analyzes the object specified as
the input parameter. The procedure tables two parameters, one for type of type of object
and the other the name of the object.
Test the procedure on the Employees table. Query the USER_TABLES to verify that the
procedure has run.
249
SQL Star International Ltd.
Write a procedure that generates a HTML page that prints Hello, World!:
5.
6.
7.
250
SQL Star International Ltd.
Chapter 12
251
SQL Star International Ltd.
Objectives
At the end of this chapter, you will be able to:
Describe the Native Compilation of PL/SQL programs
Use Autonomous Transactions
Identify Bulk Bind Enhancements of Oracle10g
Use Table Functions
252
SQL Star International Ltd.
Native Compilation
Oracle supports the following different languages:
PL/SQL
Each of these languages provides different advantages, such as ease of use, the need for
portability, or the existence of programmers with specific expertise. For instance,
PL/SQL is a powerful tool, which is specialized for SQL transaction processing, C
language is effective in executing some computation-intensive tasks and Java offers
portability coupled with security.
How would you, therefore, choose between these different implementation possibilities?
Your choice would depend on how your application intends to work with Oracle.
Only PL/SQL and Java methods / functions run within the address space of the server.
C / C++ methods / functions are dispatched as external procedures. External procedures
are procedures stored in a Dynamic Link Library (DLL) [that is, stored as a library in the
file system]. This indicates that they run on the server machine, but outside the address
space of the database server.
Prior to Oracle8.0, the Oracle database supported SQL and the stored procedure language
PL/SQL. Oracle8.0, introduced external procedures, which offered the capability of
writing C functions as PL/SQL bodies. These functions could be called from PL/SQL and
SQL. With 8.1, Oracle offered the capability of calling these external procedures from
other languages using a special-purpose interface called call specification. You could
register these external procedures with any base language (language that can call these
procedures, such as
PL/SQL, Java or C), and then call it to perform any special purpose processing. For
instance, when you register a C function with PL/SQL, the PL/SQL language loads the
library (the DLL file that contains all the C functions) dynamically at runtime, and then
calls the specified C function as if it were a PL/SQL subprogram.
In order to set external procedures written in C to use, you (as a DBA) need to take the
following steps:
1. Setup the environment in order to call external procedures by adding entries to
tnsname.ora and listener.ora files. You need to enter the agent that would handle the
external procedures. The agent is named extproc and runs on the same database server as
your application.
253
SQL Star International Ltd.
2. Identify the DLL, that is the dynamically loadable Operating System file, which stores
the external procedures. The DBA controls access to the DLL by creating a schema
object LIBRARY, which represents the DLL.
CREATE LIBRARY <library_name>
AS <file_path>;
In the LIBRARY object, you must specify the full path to the DLL. The DBA can then
grant the EXECUTE privilege on the LIBRARY object to any user.
3. Publish external procedures in a PL/SQL program through a call specification. Call
specification maps names, parameter types, and return types for the C or Java external
procedures to their SQL counterparts. The PL/SQL program is written like any other
PL/SQL stored subprogram except that, instead of declarations and BEGINEND block,
you code the AS LANGUAGE clause. For example, the code of a PL/SQL standalone
function that publishes a C function is written as follows:
CREATE OR REPLACE FUNCTION fnCallsCFunction
(/* the function finds the greatest common divisor of x and y/*
x BINARY_INTEGER,
y BINARY_INTEGER)
RETURN BINARY_INTEGER
AS LANGUAGE C
LIBRARY <library_name>
NAME <function_nametobecalled>;
There are certain disadvantages associated with external procedures. They are:
The developer has to write the C or Java program, store them in DLL files
(which have to be created explicitly), and then call them from PL/SQL
subprograms. This proves to be quite cumbersome especially if a person is
not proficient in C or Java languages.
254
SQL Star International Ltd.
PL/SQL library units are compiled into bytecode (code, which is machine
dependent), which is then loaded into the library cache part of the shared
pool in the System Global Area (SGA). Compilation of PL/SQL program units
into bytecode reduces their execution speed.
When a PL/SQL library unit is referenced by a user session, its corresponding shared
library is mapped to the virtual address space of the Oracle process. This indicates that
the compiled program is located in the PGA. When multiple users connected to an Oracle
instance reference the same natively compiled PL/SQL library unit, the shared library is
mapped to the virtual address space of each Oracle process. Since it is a shared library,
there is a single copy of the compiled PL/SQL program in the physical memory.
Benefits of Native Compilation of PL/SQL
There are two phases involved in natively compiling PL/SQL program units. These
phases are:
Native compilation improves the program execution speed due to the following reasons:
Compiled code of a PL/SQL program is mapped to the PGA instead of the SGA
into which the bytecode is loaded. This results in less contention for SGA
255
SQL Star International Ltd.
PL/SQL code not containing SQL reference is 2-10 times faster. However, it
does not speed up SQL execution
256
SQL Star International Ltd.
Native compilation is performed for a library unit, that is, for a package
specification, a package body, a top-level procedure or function. You cannot
natively compile individual procedures and functions in a package. Also,
package body and type specification must be compiled in the same mode
(that is, INTERPRETED or NATIVE).
Anonymous blocks are not natively compiled. They are always compiled for
interpreted execution. This is to avoid complications with regards to naming
the anonymous DLLs, or cleaning up these DLLs in case of a server crash.
Easy Maintenance
257
SQL Star International Ltd.
Standardizing Exceptions
Create a Standalized error handling package that includes all
programmer defined exceptions to be used in an application.
named
and
Standardizing Constants
According to definition, variable is one whose value varies, whereas a constant is one
whose value can never change. If we want local variables used in our programs not
to vary, we can convert them into constants. This makes maintenance and
debugging much easier.
Let us create a package containing constant variable and see how to access it.
CREATE OR REPLACE PACKAGE CONSTANT_PKG
IS
258
SQL Star International Ltd.
Autonomous Transactions
The word Autonomous means independent and transaction is a set of statements
that does a logical unit of work.
AUTONOMOUS_TRANSACTION is a pragma (a compiler directive), enabling PL/SQL
program units to maintain their own transaction states.
But how does it work?
An autonomous transaction starts within the context of another transaction, known
as parent transaction, though it is independent of it. This feature allows developers
to handle transactions with more ease and finer granularity. These transactions can
be committed or rolled back without affecting the parent one. i.e., in an existing
transaction, an autonomous transaction commit or roll back changes without
affecting the outcome of the main transaction.
To make the concept clear we will go through the following code snippet:
CREATE OR REPLACE PROCEDURE prautomonous
IS
cmemberid VARCHAR2(20);
BEGIN
cmember_id:=CDB028504';
COMMIT;
INSERT .......................
prindependent;
DELETE.................
COMMIT;
END;
259
SQL Star International Ltd.
They branch out of the main transaction, complete processing and then
branches in to resume the main or the calling transaction.
260
SQL Star International Ltd.
Context Switches
The figure illustrates that the PL/SQL engine executes the procedural statements, but
sends the SQL statements to the SQL engine. Therefore, every SQL statement
encountered during execution would cause a switch (known as context switch) between
the two engines. Each context switch would add to the performance overhead.
Performance overhead occurs when SQL statements are executed inside a loop using
collections (collections include varrays, nested tables or index by tables).
For instance, in the following code snippet, the DELETE statement is sent to the SQL
engine with each iteration of the FOR loop:
DECLARE
TYPE deptList IS VARRAY(20) OF NUMBER;
depts deptList := deptList(10, 30, 70);
--department numbers
BEGIN
...
FOR i IN depts.FIRST..depts.LAST
LOOP
DELETE FROM Emp WHERE deptno = depts(i);
END LOOP;
END;
261
SQL Star International Ltd.
In the code snippet, the FORALL statement is used. On encountering the FORALL
keyword, the PL/SQL engine bulk-binds collections before passing them to the SQL
engine. Its syntax is:
FORALL index IN lower_bound..upper_bound
Sql_statement;
262
SQL Star International Ltd.
You can compare the performance benefit achieved by using bulk binds. To do so, first
create a table Test, then create a PL/SQL block to insert 9000 records using a FOR loop
as well as a FORALL statement. Compare the time taken to execute the INSERT
statements.
CREATE TABLE test
(TestID NUMBER (4),
TestName VARCHAR2 (15));
Table created.
DECLARE
TYPE IDTab is TABLE OF NUMBER (4)INDEX BY BINARY_INTEGER;
TYPE
BINARY_INTEGER;
NameTab
IS
TABLE
OF
VARCHAR2(15)
INDEX
BY
vID IDTab;
vName NameTab;
vTime1 NUMBER (5);
vTime2 NUMBER (5);
vTime3 NUMBER (5);
263
SQL Star International Ltd.
FOR i in 1..9000
LOOP
INSERT INTO test
VALUES (vID(i), vName(i));
END LOOP;
prGetTime (vTime2);
FORALL i IN 1..9000
INSERT INTO test
VALUES (vID(i), vName(i));
prGetTime (vTime3);
DBMS_OUTPUT.PUT_LINE(Execution
Time Taken(in secs));
DBMS_OUTPUT.PUT_LINE();
DBMS_OUTPUT.PUT_LINE(FOR loop:
|| TO_CHAR(vTime2 - vTime1));
DBMS_OUTPUT.PUT_LINE(FORALL:
|| TO_CHAR(vTime3 - vTime2));
END;
/
In the example, 9000 names and IDs are loaded into index-by tables. Next, all table
elements are inserted into the database table Test twice. First, they are inserted using the
FOR loop, which takes 5 seconds. Next, they are bulk inserted using a FORALL
statement, which takes just 1 second.
On executing the block, the following result is displayed:
Execution Time Taken (in secs)
--------------------------------FOR loop: 5
FORALL:
were rolled back. For instance, you created a table Product that stored product number
and product name as follows:
CREATE TABLE Product
(ProdNo NUMBER(2),
ProdName VARCHAR2(15));
Table created.
You inserted few records into the table, such as:
PRODNO PRODNAME
------ ---------10
Mouse (5 characters length)
20
30
40
In the block, the UPDATE statement would be executed thrice by the SQL engine, once
for each index number in the specified range. That is, once for product number 20, once
for product number 30, and once for product number 40. The first execution would be
successful, but the second execution would fail because the value Processor Reorder is
large for the product name column. In such a case, only the second execution would be
rolled back, and the third execution would never be done.
265
SQL Star International Ltd.
Oracle10g has incorporated FORALL Support for Non-Consecutive Indexes for more
convenient and efficient bulk bind operations.
Earlier, Oracle9i had incorporated an error handling mechanism so that exceptions raised
during a bulk bind operation are collected and returned together once the operation is
complete.
That is, it enables bulk-bind operation to save information regarding exceptions and
continue processing.
In order to complete the bulk bind operation despite errors, add the keywords SAVE
EXCEPTIONS in the FORALL statement. The syntax is as follows:
FORALL index IN lowerbound..upperbound
[SAVE EXCEPTIONS]
sql_statements;
All errors occurring during the execution are saved in a new cursor attribute
%BULK_EXCEPTIONS. %BULK_EXCEPTIONS store values that always refer to the
most recently executed FORALL statement. This attribute stores a collection of records,
where each record has two fields:
The first field is %BULK_EXCEPTIONS (i). ERROR_INDEX, stores the iteration of the
FORALL statement during which the exception occurred.
The second field is %BULK_EXCEPTIONS (i). ERROR_CODE, which stores the
corresponding Oracle error code, SQLCODE. You can get the corresponding error
message by calling SQLERRM with SQL error code as the parameter.
The number of exceptions that have occurred during the execution of the FORALL
statement is saved in the count attribute of %BULK_EXCEPTIONS (that is,
%BULK_EXCEPTIONS.COUNT). Its values range from 1 to COUNT.
The following code shows the use of the cursor attribute %BULK_EXCEPTIONS:
DECLARE
266
SQL Star International Ltd.
In the code, PL/SQL raises the ZERO_DIVIDE exception when i=0. The exception
occurs when the iteration equals 3 and 6. But since SAVE EXCEPTIONS keyword is
used, the bulk bind operation gets completed. Once the bulk bind operation is complete,
SQL%BULK_EXCEPTIONS.COUNT returns 2, and SQL%BULK_EXCEPTIONS
contains the information about the exception such as (3, 1476) and (6, 1476). The output
on executing the code is:
No. of errors is: 2
Error 1 occured during iteration 3
The Oracle error is: -1476: non-ORACLE exception
Error 2 occured during iteration 6
The Oracle error is: -1476: non-ORACLE exception
267
SQL Star International Ltd.
INDICES OF s_no
268
SQL Star International Ltd.
member_tab;
The c_memberid INDEX BY table is used to stored values of the member id at the index
number 10, 12 and 14 respectively.
The v_memberid INDEX BY table, holds the index number of the c_memberid Index
by table, but at the index number 1,2 and 3.
Now, using a VALUES OF clause along with v_memberid Index by table in the
FORALL statement, passes the value to the c_memberid index by table used in the
WHERE clause of the UPDATE statement thereby updating the member id.
269
SQL Star International Ltd.
IMMEDIATE
SELECT
cMemberID
FROM
Member
WHERE
ROWNUM<=15
BULK COLLECT INTO memberids;
END;
/
You can bulk bind input variables of a SQL statement using the FORALL statement and
the USING clause as shown below.
DECLARE
TYPE tyIDsList IS TABLE OF CHAR(5);
TYPE tyNameList IS TABLE OF CHAR(15);
IDs tyIDsList:=tyIDsList(02CRI,02SOC,02ADM);
Names tyNameList:=tyNameList(Criminology,
Sociology,Administration)
BEGIN
FORALL i IN 1..3
EXECUTE IMMEDIATE
INSERT INTO Category
VALUES (:1,:2)
USING IDs(i),Names(i);
END;
/
In the code, bulk binding is used to insert records into the Category table. Within the
FORALL statement the EXECUTE IMMEDIATE statement is used to execute an
INSERT statement. Two input variables IDs and Names containing the category IDs and
Category names respectively are passed to the INSERT statement using the USING
270
SQL Star International Ltd.
clause. This way 3 rows are created using one INSERT statement with one call to the
database.
You can bulk bind output variables by using the RETURNING BULK COLLECT INTO
clause as shown below.
DECLARE
TYPE tyAge IS TABLE OF NUMBER;
AgeTab tyAge;
vSqlStmt VARCHAR2(200);
vBrID CHAR(7):=01ADLNJ;
BEGIN
vSqlStmt:=UPDATE Member
SET nAge=nAge+1
WHERE cBranchID=:1
RETURNING nAge INTO :2';
EXECUTE IMMEDIATE vSqlStmt
USING vBrID
RETURNING BULK COLLECT INTO AgeTab;
END;
/
In the code, the output variable AgeTab is bound through the RETURNING BULK
COLLECT INTO clause. The EXECUTE IMMEDIATE statement is used to update the
ages of members belonging to branch 01ADLNJ. An input variable vBrID is used to pass
the branch ID that is to be used as the criteria for the update. All the updated ages are
then returned using AgeTab collection variable.
271
SQL Star International Ltd.
Table Functions
To improve the performance of a program, it is necessary to decrease the response
time. This can be achieved using Table Functions.
It does the following:
-> Accept a stream of rows as inputs
-> Pipeline the output
-> Parallelize the evaluation of table functions
You can define table functions in PL/SQL using native PL/SQL interface, or in Java or
C using the Oracle Data Cartridge Interface (ODCI).
3. Create a package pk1 with package variables of record type and ref cursor type
CREATE OR REPLACE package pk1
AS
TYPE recMember IS RECORD (cMemberID CHAR(9), cLastName
CHAR(20));
TYPE refcurMember IS REF CURSOR RETURN recMember;
END;
4. Create a function fn to transform each record corresponding to the cursor passed
as the parameter, and return the corresponding row to the caller.
CREATE OR REPLACE FUNCTION fn
(vParam IN pk1.refcurMember)
RETURN objtypeTable
PARALLEL_ENABLE(PARTITION vParam BY HASH(cMemberID))
272
SQL Star International Ltd.
PIPELINED IS
var1 pk1.recMember;
BEGIN
LOOP
FETCH vParam INTO var1;
EXIT WHEN vParam%NOTFOUND;
PIPE ROW(objtype(var1.cMemberID));
END LOOP;
RETURN;
END fn;
In the code, the function returns one record as soon as one row is processed. The
function uses the new PIPELINED instruction, which causes:
->A single result row to be returned
->The execution of the function to be suspended
->The function to restart when the caller requests the next row
In the code an optimization hint (optional) PARALLEL_ENABLE is used, which
indicates that you can execute the function in parallel. To do so, you must ensure
that the function does not use session state, such as package variables because
these variables cannot be shared among parallel execution servers.
In the code, PARTITION argument BY clause is used. It is an optional clause, and is
used with functions that have REF CURSOR type argument. With this clause you can
define the partitioning of inputs from the REF CURSOR argument to the function. This
affects the way the query is parallelized when the function is used in the FROM
clause of the query.
5. Execute the following SELECT statement:
SELECT * FROM
Member)));
TABLE(fn(CURSOR(SELECT
cMemberID,
cLastName
FROM
The pipelined function is used in the FROM clause of the SELECT statement. From the
above-mentioned steps, it can be concluded that:
The producer function (in our case, fn) uses the PIPELINED keyword in its
declaration.
When each record is processed, it is sent to the consumer function (or the SQL
statement) using the PIPE ROW keyword.
273
SQL Star International Ltd.
The producer function has a RETURN statement that does not return any values
The consumer function (or the SQL statement) uses the TABLE keyword, which
treats the resulting rows like a regular table
Table functions pipe the results as soon as they are produced. This reduces
the response time.
Table functions eliminate the need for buffering the produced rows.
Summary
In this chapter, you have learnt that:
274
SQL Star International Ltd.
In Bulk Binding, a set of SQL statements are bound as a collection and sent to
the SQL engine rather than as individual elements. This reduces performance
overhead.
Use of FORALL statement has been improved with clauses like SAVE
EXCEPTION (used to complete bulk binding despite errors), INDICES OF clause
(introduced in Oracle10g to handle Non-Consecutive Indexes), and VALUES OF
clause.
Table Functions accept a stream of rows as input and pipeline the output. This
decreases the response time thereby improving the performance.
Lab Exercises
1.
Use bulk dynamic SQL to insert the following four sets of values into the
Countries table:
Country ID: DU
FI
MA
PH
Finland
Malaysia
Philippines
Verify the rows inserted into the Countries table.
2.
Experiment the usage of Autonomous Transaction through the following series
of questions.
a. Verify that you are logged into your SCOTT schema by using show
command in SQL *Plus:
b. Create a procedures as given below
275
SQL Star International Ltd.
department_name=UPPER(department_name);
UPDATE
employees
SET last_name=UPPER(last_name);
COMMIT;
END modify_dept_emp;
/
Create and Save the script in a file.
Run the script to create the procedures.
276
SQL Star International Ltd.
j. What are the results? Which statements are committed and which
statements are rolled back?
3.
Create a PL/SQL composite variable and assign few employee names into it. Delete
the 3rd indexed data. Now, insert the data into database table Emp_Names.
Handle the errors using Bulk save exception clause and display them.
4.
Create a table function that will return the employee ID from the Employees table.
277
SQL Star International Ltd.
Chapter 13
Large Objects
LONG vs. LOB
LOB Datatypes
Migration of LONG to LOB
Using LOBs
DBMS_LOB Package
Removing LOBs
278
SQL Star International Ltd.
Objectives
At the end of this chapter, you will be able to:
List the different datatypes for large objects
Use LOBs
Migrate from LONG to LOBs
279
SQL Star International Ltd.
Large Objects
One of the reasons for using databases is that it can store large amount of data.
Special database objects were built to store huge chunks of data. You know of
abstract datatypes that store data according to your requirement. Now you will use
objects referred to as Large Objects or LOBs. These are also datatypes but should
not be confused with object types learnt earlier. With the use of LOBs, you can use
large amount of data that need not necessarily be stored inside the database.
LONG Vs LOB
LONG and the LONG RAW datatypes are used to store large unstructured data. These
datatypes could hold binary data, images or documents. Then one might wonder why
LOBs were introduced. The reasons why LOBs are used bring out the following
differences between LONG and LOB.
A table is allowed only one LONG datatype column, but as many LOB
columns.
While querying, LONG returns the data but LOBs return the data locators.
Data stored as LONG is stored within the same datablock in the database.
LOBs store the locator of the data if the data is more than 4000 bytes. The
data is stored in separate host files or different tablespaces.
Object types cannot have LONG attributes but can have LOB attributes.
Data access is sequential in the case of LONG data and random in the case of
LOB data. In the latter, data is accessed through a file-like interface.
LOB Datatypes
LOBs are categorized based on the way they are stored and the way they are
interpreted by the Oracle server. They are also classified depending on the kind of
data they hold. In Oracle10g the maximum size limit for LOBs has been increased
from 4 GB to 8 - 128 terabytes, depending on your database block size.
Given below are the four LOB datatypes:
BFILE or Binary File is a file that stores data outside the database up to a limit
specified by the operating system. Data in BFILE is stored as read-only data.
280
SQL Star International Ltd.
External LOBs that are stored outside the database, like BFILEs.
Internal LOBs
The following can be assigned as internal LOBs:
Columns of table
281
SQL Star International Ltd.
External LOBs
External LOBs refer to data, which are often known as BFILEs. BFILEs are large
objects, which may be of formats such as GIF, JPEG, MPEG or text and are stored in
operating system files outside the database tablespaces. The external LOBs may be
located on hard disks or CDROMs, but a single external LOB cannot extend from one
device to another.
The BFILE datatype enables database users to access the external file system.
The BFILE datatype contains a locator that points to the file in the file system that
holds the main data. The locator stores the directory alias and the file name.
The Oracle server was faced with the issue of securing data due to the unauthorized
access to files on a server. To overcome this problem, a security mechanism was
introduced in Oracle9i, wherein, it shields the operating system from unauthorized
access, and does away with the need to manage additional user accounts on an
enterprise computer system. Users can read from BFILEs the same way as they
would from an internal LOB. But there are certain restrictions imposed on the file
itself, which are with regard to:
Access permissions
282
SQL Star International Ltd.
The DIRECTORY object specifies an alias for the directory on the file system of the
server within which a BFILE is located. You can provide secure access to files within
the corresponding directories on a user-by-user basis by granting the appropriate
privileges. For instance, you can make some directories read-only or inaccessible.
DIRECTORY object privileges can be granted and also revoked.
The DIRECTORY object is owned by SYS. The DBA or any user with the CREATE ANY
DIRECTORY privilege can create the DIRECTORY object.
Points to remember while creating DIRECTORY objects are:
Grant the system privileges CREATE ANY DIRECTORY and DROP ANY
DIRECTORY to users discriminately.
In case you migrate the database to a different OS, you would have to
change the path value of the DIRECTORY object.
Managing BFILEs
You interact with BFILEs in the same way as with any other LOB. The steps you need
to follow to work with BFILE and DIRECTORY objects are:
1. Creating the directories in the operating system and setting their permissions
so that Oracle can read them.
2. Loading files into the directories created.
3. Creating a table with the BFILE datatype column in the Oracle server.
4. Creating the DIRECTORY objects with READ permission.
5. Inserting data into the table with the BFILENAME function. The function takes
2 parameters.
1. Directory Name
2. OS Filename
6. Declaring and initializing LOB locators.
7. Set the row and column with the BFILE value into the locator.
8. Read the BFILE data using the OCI or the DBMS_LOB package interface.
283
SQL Star International Ltd.
284
SQL Star International Ltd.
Passing of LOBs to SQL and PL/SQL built-in functions and operator: API
supports the passing of a CLOB variable to SQL and PL/SQL VARCHAR2 builtin functions behaving just like a VARCHAR2. Similarly, VARCHAR2 variables
can be passed into DBMS_LOB package acting like a LOB locator.
Clustered tables do not support LOB columns (LONG and LONG RAW columns
are allowed in clustered tables). Hence, the LONG and LONG RAW columns of
a table that is part of a cluster cannot be changed to CLOB or BLOB.
The UPDATE OF list of an update trigger does not support LOB columns.
Therefore, when a LONG column is converted to a LOB column, triggers that
were intended to fire on the update of a LONG column becomes invalid.
Before migrating to LOB columns, domain indexes on LONG and LONG RAW
columns must be dropped.
285
SQL Star International Ltd.
Structure of LOBs
A LOB has two parts. One is the value of the LOB. This is the actual data that is held
by the LOB. The other is the locator, which is a pointer to the data stored by the
LOB. LOBs does not store the data values directly, but stores a pointer to the data
instead.
If you write a program to manipulate or access LOBs, you need to declare a pointer
or a LOB locator within it. An internal LOB data is stored in a LOB segment and the
LOB column value for that row is the pointer or locator to the data. If the LOB data is
stored externally, then only the pointer is stored in the table. Programs to access
and manipulate LOB data use these pointers.
Using LOBs
LOBs are used just like any other datatype. The difference is in the amount and
method in which the data is stored.
Creating LOB Columns
There is no special syntax for creating columns of LOB datatypes. Like any other
datatype, they are assigned to columns at the time of table creation.
Returning to the library database, a librarian at the main branch thought it would be
a good idea to store a short description of each book and have a picture of its cover
page. They will have to include new columns in the Book table. Instead, they
thought they could first create a table for the purpose and see how useful it is.
Hence, the following table was created:
286
SQL Star International Ltd.
0)
TABLESPACE BookDesc;
287
SQL Star International Ltd.
Inserting Values
You insert values into LOB datatypes directly using variables. You can perform
updates on LOB columns by setting the value to be modified to another LOB value or
to an empty locator using the EMPTY_CLOB () or EMPTY_BLOB () functions. Syntax
to use this function is:
INSERT INTO <table_name>(column1,column2,CLOBcolumn,
BLOBcolumn,)
VALUES (value1,value2,EMPTY_CLOB(),EMPTY_BLOB(),);
Where,
CLOBcolumn, BLOBcolumn are two LOB columns in a table
EMPTY_CLOB(),EMPTY_BLOB() are the functions to set empty locators to the two
LOB columns respectively
A point to remember is that, before manipulating the value the row must be locked.
You lock a row by using the FOR UPDATE clause.
Ensure that LOB columns must have either empty locators or a value.
To insert values into LOBs in PL/SQL, you use one variable to set the LOB locator and
another variable to set the length of the text to be inserted into the LOB. Use the
DBMS_LOB package to manipulate LOBs.
For instance, to insert data into the CLOB for book reviews execute the following
code:
DECLARE
clLocator CLOB;
vData VARCHAR2(32766);
nDataLength NUMBER;
iDataOffset INTEGER;
BkID CHAR(13):=&bkid;
BEGIN
vData:=text to be entered into the LOB;
288
SQL Star International Ltd.
SELECT clBookReviews
INTO clLocator
FROM BookDesc
WHERE BookID=BkID
FOR UPDATE;
iDataOffset:=DBMS_LOB.GETLENGTH(clLocator)+1;
nDataLength:=LENGTH(vData);
DBMS_LOB.WRITE(clLocator, nDataLength, iDataOffset, vData);
COMMIT;
DBMS_OUTPUT.PUT_LINE(TO_CHAR(nDataLength)||bytes have been
written to the LOB);
END;
/
In the code, the four variables declared are to respectively store the locator of the
data, the data, the size of the data and the point at which the data should start. The
SELECT INTO clause enters the locator value for a particular record in the table,
depending on the condition.
The DBMS_OUTPUT.GETLENGTH () procedure finds out the point in the block from
where the LOB data can be written and stores this in a variable.
The DBMS_LOB.WRITE () procedure passes the four variables and writes the data to
the LOB at the offset specified.
The write is committed. DBMS_OUTPUT.PUT_LINE () procedure displays a message
on the screen as to how much data has been inserted.
To insert empty locators into the LOB columns in the table, use the functions
EMPTY_CLOB () or EMPTY_BLOB () according to the datatype. Entering an empty
locator is not the same as entering a NULL value. In case the value of a locator is
NULL, you need to update it with the empty locator to change its status to NOT
NULL.
289
SQL Star International Ltd.
Updating LOBs
Updates to LOBs are done in the same manner that the traditional datatypes are
updated. The UPDATESET statement is used to perform the required
modifications. In case Peter, a librarian wants to modify some of the review
comments that have been entered about a book, he can execute the following
statement to first assign an empty locator to the column and then update the value.
UPDATE BookDesc
SET clBookReviews=EMPTY_CLOB()
WHERE BookID=CLS050005777';
This assigns the empty locator and now he may use the UPDATE statement to enter
a new value.
DBMS_LOB Package
You can modify values stored in columns of LOB datatypes using the DBMS_LOB
package as already dealt with when inserting values into LOBs. There are a set of
functions and procedures to select and modify data from a LOB.
These functions and procedures are classified into two types. They are:
Mutators, which can modify LOB values: WRITE, APPEND, COPY, TRIM,
ERASE, FILECLOSE, FILECLOSEALL and FILEOPEN
Observers, which can only read LOB values: READ, GETLENGTH, COMPARE,
FILEGETNAME, FILEEXISTS, FILEISOPEN, INSTR and SUBSTR
READ: To read LOB data. For this it uses, the locator, size of data to be read,
the offset (starting point of read) and an output variable to hold the output,
as parameters.
TRIM: Performs the same task as the RTRIM function in SQL. You can use this
function if you want to reduce the size of your LOB. You need to pass the
locator and the new required size of the LOB as parameters.
WRITE: To write data into a LOB. The parameters to be passed with this
procedure are the locator value, the number of characters to be written, the
290
SQL Star International Ltd.
offset to start the write and the buffer variable assigned to the data being
added.
APPEND: To add the contents of one LOB value to another in case one LOB
requires data from another. For this you pass the two LOB locators as
parameters, one for the source and the other for the destination.
WRITEAPPEND: To write data into a LOB from the point where the current
data ends.
ERASE: Removes a part of or the entire LOB. You pass the locator, amount of
data to be erased, and the offset where the erase needs to start.
COPY: A procedure to copy contents from one file to another. It uses the
locators of the two LOB files, the size of the data to be copied and the offsets
for the two files. The offset of the source file indicates from where the copying
should start and that of the destination file specifies from where the data
should be pasted.
These procedures can be used when you want to perform a set of transactions and
require working with lot of files. You can open a file during execution of the PL/SQL
block. You can check whether the file is a temporary one and if so then the set of
statements to perform operations on it can be different.
The functions to manipulate LOB values include:
COMPARE: Compares two LOB values. There parameters that are used are,
the locators of the two files, the number of characters to be compared, and
the offsets of the two files.
GETLENGTH: Determines the length of the data. It is the same as the SQL
LENGTH function. This function takes the locator as a parameter.
INSTR: Performs the same function on a LOB that the INSTR function does in
SQL. It has four parameters of the locator, the pattern to be checked, the
offset or starting point of the read, and the occurrence of the pattern. This
function can help you locate particular information within the large text files.
SUBSTR: Performs the same function on a LOB that the SUBSTR function
does in SQL. It takes the parameters of locator, the number of characters to
be read, and the offset.
These functions allow you to make modifications to the data that has been read from
the file. These modifications happen before the user reads the data.
291
SQL Star International Ltd.
There are some functions and procedures specific to BFILEs. These include:
FILEGETNAME: Gets the name of the external BFILE to which the locator
points.
These procedures are used in PL/SQL blocks written to perform operations with
LOBs. Variables are declared and they are used to either store results of the
functions or used as parameters to perform operations.
All the functions and procedures need to be preceded by the keyword DBMS_LOB.
All the DBMS_LOB functions return NULL if any of the input parameters is NULL. All
the DBMS_LOB procedures, which can modify LOB values, raise an exception in case
the destination of BFILE is input as NULL.
You can implement one of the procedures to know how the rest of them need to be
used. If you want to add the review comments of a critic to the review comments of
a book with id CLS040005776, you use the APPEND procedure as shown.
DECLARE
clDestination CLOB;
clSource CLOB;
BEGIN
SELECT clBookReviews
INTO clDestination
FROM BookDesc
WHERE BookID = CLS040005776
FOR UPDATE;
SELECT clBookReviews
INTO clSource
FROM BookDesc
WHERE BookID = CLS050005777;
292
SQL Star International Ltd.
DBMS_LOB.APPEND(clDestination, clSource);
COMMIT;
END;
/
Removing LOBs
LOB values can be removed by using the DELETE statement. This will, as you know
delete the record from the table. In case you want to keep the record and only
remove the LOB value, then replace the LOB value (the locator) in the table with a
NULL or an empty locator. The latter is possible by using the
EMPTY_BLOB/CLOB/NCLOB () function. It must be noted that, these two options do
not amount to the same value in the column. NULL is a null value, but using the
empty locator ensures that there is nothing stored in the column for that record, not
even a NULL value.
Deleting a record, truncating or dropping a table will result in the LOB getting
destroyed.
In case of a BFILE, the process is the same when working on the database table, but
the external BFILE needs to be explicitly removed from the operating system using
the OS commands.
293
SQL Star International Ltd.
Summary
In this chapter, you have learnt that:
Large Objects (LOBs) store Large chunks of data. They can store more than 4GB
of binary, character, image data etc.
BLOB(that stores Binary data), CLOB(that stores Character data), BFILE( Binary
file stored outside the database) and NCLOB(National Character Large Object to
store multibyte character set) are the different types of LOBs.
BLOB, CLOB and NCLOB are called Internal LOBs as they are stored inside the
database. BFILE is called External LOB since it is stored outside the database.
To facilitate migration from LONG to LOB, Oracle9i introduced the LONG API for
LOBs.
To convert other datatypes to CLOB and BLOB, TO_CLOB() and TO_BLOB()
functions are used.
LOBs do not store values directly but store a locator or a pointer to the data
instead.
DBMS_LOB package has a set of procedures and functions to select and modify
data from an LOB.
294
SQL Star International Ltd.
Lab Exercises
1.
Create a table called EmpPersonalDetails. The structure of the table should
be as follows:
2.
Insert two rows into the EmpPersonalDetails table. The rows must have IDs
as 501 and 502 respectively. For CLOB use the empty function and for BLOB provide
NULL.
3.
4.
Populate the first row of CLOB column Review by using the UPDATE
statement
Query the EmpPersonalDetails table to retrieve the employee number, last name
and review details.
295
SQL Star International Ltd.
Chapter 14
Database Triggers
Permissions to Create Triggers
Guidelines to Design Triggers
DML Trigger Components
Trigger Execution Mechanism
Creating Statement Level Triggers
Creating Row Level Triggers
Creating INSTEAD OF Trigger
296
SQL Star International Ltd.
Objectives
At the end of this chapter, you will be able to:
Identify trigger components
Create database triggers
297
SQL Star International Ltd.
Introducing Triggers
You know how a gun works. Pull the trigger and the bullet fires. Meaning, the action
of pulling the trigger causes something to happen. Similarly, there are some
transactions that need to be performed as a result of another transaction. This kind
of functionality is implemented by using a database object called trigger.
Database Triggers
Database triggers can be defined as a block of PL/SQL code that gets executed
automatically based on an event in an application. An event refers to any DML on a
table (such as INSERT, UPDATE or DELETE triggering statements), or DDL
statements such as CREATE and ALTER. Triggers are also executed implicitly on the
occurrence of some user actions or database system actions. Examples of such
situations are when a user logs on, or when the database is shutdown by the DBA.
Database triggers can be classified as system triggers on a database and system
triggers on a schema. In case of system triggers on a database, triggers fire for each
event for all users, whereas in case of system triggers on a schema, triggers fire for
each event for a specific user.
You can define database triggers on tables and also on views. But, if actions on the
view cause DML statements to be executed on the base table, then the triggers
associated to that table are also fired apart from the triggers associated with the
view.
Permissions to Create Triggers
To be able to create triggers on a table, you need to have permissions to modify the
table. To create triggers on tables you would need:
Below is a list of situations in which you should create triggers and those when you
should not.
298
SQL Star International Ltd.
Use triggers to ensure that when a specific operation takes place, related
actions are performed.
Use triggers only for global operations that should be fired for triggering
statements, irrespective of the user or application that issues the statement.
Using too many triggers could lead to interdependencies and this is difficult to
maintain. Hence, use triggers only when required. Also take care of the
recursive or cascading effects that triggers could have.
In case of lengthy trigger logic, create stored procedures with the logic and
invoke the procedure in the trigger body.
Trigger Timing
Trigger Events
Trigger Type
Trigger Code
Trigger Timing
Trigger timing indicates when the trigger fires with respect to the triggering event.
Depending on when the trigger should fire, triggers are categorized into the following
three types:
BEFORE triggers:
The trigger body is executed before the triggering DML event on a table. BEFORE
triggers are used mainly to:
299
SQL Star International Ltd.
AFTER triggers:
The trigger body is executed after the triggering DML event on the table. AFTER
triggers are used mainly to complete the triggering statements before the
triggering actions are performed.
INSTEAD OF triggers:
Instead of the triggering statement, the trigger body is executed. INSTEAD OF
triggers are used to modify views, which otherwise cannot be modified directly
using SQL DML statements. When you try to execute INSERT, UPDATE and
DELETE statements on a view, the INSTEAD OF trigger works in the background
to perform the actions that are coded in the trigger body directly on the
underlying base table.
Triggering Events
Triggering event indicates which DML operation on the table or view causes the
trigger to fire. These events include the three DML statements: INSERT, UPDATE and
DELETE. You can write triggers to fire on the execution of any one or a combination
of the three DML statements.
In case the event is an UPDATE statement, a column list specifying which columns
must be updated in order to fire the trigger can be included. The same is not possible
in case of an INSERT or a DELETE statement as they are implemented on the entire
row in a table and not on selected columns.
Trigger Types
Triggers have been classified into two types based on how many times the trigger is
fired, that is, whether the trigger body is executed for each row the triggering
statement affects or is executed only once. The two types of triggers are:
Row Triggers
Row triggers cause the trigger body to be executed once for each row affected by
the triggering event. In case no rows are affected by the triggering event, the
row trigger is not executed. These triggers are useful when the trigger action
depends on the data of rows that are affected by the triggering event.
Statement Triggers
Statement triggers causes the trigger body to be executed only once for the
triggering event. This trigger does not depend on the data of rows affected and
hence fire once even if no rows have been affected. This is the default trigger.
This type of trigger is used mainly to perform complex security check on users.
Trigger Code
Trigger code contains what action needs to be performed when the triggering event
is issued. The trigger code is nothing but a PL/SQL block, which can contain SQL and
300
SQL Star International Ltd.
PL/SQL statements, variables, cursors and also exceptions. It can also contain a call
to a procedure.
Remember that a trigger size cannot exceed 32K.
301
SQL Star International Ltd.
[<schema_name>| <database_name> |
Statement level triggers, as you know fire once for the entire transaction. The syntax for
these triggers is:
CREATE [OR REPLACE] TRIGGER [user.]<trigger_name>
{timing}
{DML event1 [OR event2 OR event3. . .]}
ON <table_name>
PL/SQL block;
Where,
[OR REPLACE] is to be used when you want to modify the definition of an already
existing trigger.
trigger_name is the name of the trigger.
timing specifies when you want the trigger to fire BEFORE, AFTER or INSTEAD OF.
302
SQL Star International Ltd.
DML events are any of the INSERT, UPDATE, DELETE or any combination of them.
table_name is the name of the table to which the trigger is associated.
PL/SQL block is the set of statements that defines what the trigger should do. This is
the trigger code.
303
SQL Star International Ltd.
SCOTT.TRIGLIBTRANSECURITY
Very often, you may be unaware of which DML transaction will be performed on a
table. Therefore, you would have to create triggers for all the three situations with
different functionality for each trigger if required.
There are two ways to do this:
Using conditional predicates and creating one trigger for that table. The three
conditional predicates are:
INSERTING
UPDATING
DELETING
The following code shows the trigger using the conditional predicates:
CREATE OR REPLACE TRIGGER trigLibMemSecurity
BEFORE INSERT OR UPDATE OR DELETE ON Member
BEGIN
IF USER <> CLERK THEN
IF INSERTING THEN
RAISE_APPLICATION_ERROR(-20012, You cannot
insert member details);
ELSIF UPDATING THEN
RAISE_APPLICATION_ERROR(-20013, Cannot update
member details);
ELSIF DELETING THEN
RAISE_APPLICATION_ERROR(-20014, Cannot delete
any details from the Member table);
END IF;
END IF;
END;
/
304
SQL Star International Ltd.
Trigger created.
Only the user CLERK has the access privilege to work with the Member table.
Writing the above trigger ensures this. The trigger prevents any user other than
CLERK to manipulate the member data. By using the conditional predicates each of
DML operations are given appropriate messages.
Creating Row Triggers
Row triggers are fired for every row of data that is affected by the triggering
statement. The syntax to create a row trigger is
CREATE [OR REPLACE] TRIGGER [user.]<trigger_name>
{timing}
{DML event1 [OR event2 OR event3. . .]}
ON <table_name>
[REFERENCING OLD AS old | NEW AS new]
FOR EACH ROW
[WHEN condition]
PL/SQL block;
Where,
[OR REPLACE] is to be used when you want to modify the definition of an already
existing trigger.
trigger_name is the name of the trigger.
timing specifies when you want the trigger to fire BEFORE, AFTER or INSTEAD OF.
DML events are any of the INSERT, UPDATE, DELETE or any combination of them.
table_name is the name of the table to which the trigger is associated.
[REFERENCING OLD AS <old> | NEW AS <new>] the old and new data are referred
to with some names for correlation. The default is old and new.
FOR EACH ROW specifies that it is a row trigger and should fire after or before every
row affected by the triggering statement.
[WHEN condition] is to specify the condition for the trigger action to take place. If
the condition is met the trigger code is executed else it is not.
PL/SQL block is the set of statements that defines what the trigger should do.
In row triggers, column values before and after data change are referenced by
prefixing them with the qualifiers OLD and NEW respectively.
305
SQL Star International Ltd.
The before and after values of columns in case of the different data operations are given
below.
When referencing the qualifiers in SQL and PL/SQL statements, prefix them with a
colon. However, when referencing the qualifiers in the WHEN condition, do not prefix
them with a colon.
age of five);
END IF;
END;
/
Trigger created.
306
SQL Star International Ltd.
The above trigger will prevent a person from being admitted as a member of the
library if his age is below 5. Similarly, the trigger will not allow the age of a member
to be updated if it is below 5.
If you attempt to perform the following, the trigger raises an error:
UPDATE Member
SET nAge = 4
WHERE cMemberID = AVE119705;
UPDATE Member
*
ERROR at line 1:
ORA-20111: Member cannot be below the age of five
ORA-06512: at SCOTT.TRIGCHKAGE, line 4
ORA-04088: error during execution of trigger SCOTT.TRIGCHKAGE
307
SQL Star International Ltd.
THEN
RAISE_APPLICATION_ERROR (-20002, Number of copies is
not within the expected range -||vMinCopies|| and
||vMaxCopies);
END IF;
END;
/
Trigger created.
This trigger trigchkcopies will prevent the number of copies of books stored in
branch 01ADLNJ to be updated below the minimum number of copies stored in the
branch or updated above the maximum number of copies stored in the branch. The
trigger should enforce the same restriction in case a new row is inserted into the
Book table for branch 01ADLNJ.
Testing the trigger trigChkCopies
You can check the maximum and minimum number of copies of books stored in
branch 01ADLNJ by querying the Book table before testing the trigger. The output
of the query would be as follows:
308
SQL Star International Ltd.
ERROR at line 1:
ORA-20002: Number of copies is not within the expected range -2
and 9
ORA-06512: at SCOTT.TRIGCHKCOPIES, line 11
ORA-04088: error during execution of trigger
SCOTT.TRIGCHKCOPIES
309
SQL Star International Ltd.
WHERE Table_Name=Member
AND Column_Name is NULL;
ELSIF DELETING THEN
UPDATE MemberAudit
SET Del = Del + 1
WHERE Table_Name=Member
AND Column_Name IS NULL;
ELSIF UPDATING (vAddress) THEN
UPDATE MemberAudit
SET Upd = Upd + 1
WHERE Table_Name=Member
AND Column_Name = vAddress;
ELSIF UPDATING (cPhone) THEN
UPDATE MemberAudit
SET Upd = Upd + 1
WHERE Table_Name=Member
AND Column_Name = cPhone;
ELSE
UPDATE MemberAudit
SET Upd = Upd + 1
WHERE Table_Name=Member
AND Column_Name IS NULL;
END IF;
END;
/
Trigger created.
The trigger trigAuditMember will update an audit table MemberAudit to keep a
count of the inserts, deletes and updates of vAddress and cPhone columns
performed by different users on the Member table.
310
SQL Star International Ltd.
311
SQL Star International Ltd.
312
SQL Star International Ltd.
CREATE
Where,
trigger_name is the name of the trigger
INSTEAD OF is the type of trigger used.
DML event1 are any of the DML statements of INSERT, UPDATE or DELETE.
view_name is the name of the view that the trigger is associated with
[REFERENCING OLD AS old | NEW AS new] specifies the correlating names for the
old and new data.
[FOR EACH ROW] is specified to designate the trigger to each row as is done in case
of row triggers.
PL/SQL block is the code of SQL and PL/SQL statements that defines the transactions
that the trigger should perform.
2. Populate the nCountAge column with values taken from the Member
table as shown below.
UPDATE MemberFee
SET nCountAge = (SELECT COUNT (nAge)
FROM Member
4. Create an INSTEAD OF trigger such that when a row is inserted into the
view vwMemberGrade, instead of inserting the rows into the view, the rows
are added to the Member and MemberFee table.
CREATE OR REPLACE TRIGGER trigVwMemberGrade
INSTEAD OF INSERT ON VwMemberGrade
FOR EACH ROW
BEGIN
314
SQL Star International Ltd.
315
SQL Star International Ltd.
Trigger Modes
Mode of a trigger is the state a trigger is in, that is, whether it is enabled or disabled.
When a trigger is created it is enabled by default. When a trigger is enabled, the
Oracle server provides some resources to the trigger like, locking the tables for read
consistency, managing dependencies, and also performing a two-phased commit if
the trigger performs updates on remote tables.
If you do not want the trigger to fire, then you have to explicitly disable it using a
command.
You enable / disable a trigger with the following syntax:
ALTER TRIGGER <trigger_name> DISABLE | ENABLE;
If you want to enable / disable all the triggers on a particular table, then use the following
syntax:
ALTER TABLE <table_name> DISABLE | ENABLE ALL TRIGGERS
When you dont want the data integrity checks to be performed when loading
a huge amount of data.
When the table associated to the trigger is not available due to various
reasons like no network connection, system hard disk crash, and offline data
file or offline tablespace.
Recompiling Triggers
Triggers are compiled like any other PL/SQL block.
When the trigger is compiled without any errors you will get a message, saying that
the trigger has been created.
In case a trigger is invalid, you need to explicitly recompile the trigger. This is done
using the ALTER TRIGGER statement. The ALTER TRIGGER statement with the
COMPILE option compiles the trigger irrespective of the fact that the trigger is valid
or invalid. The syntax for the same is:
ALTER TRIGGER <trigger_name> COMPILE
316
SQL Star International Ltd.
317
SQL Star International Ltd.
Summary
In this chapter, you have learnt that:
You need to have certain privileges (Like CREATE TRIGGER, CREATE ANY
TRIGGER, ALTER TRIGGER etc) to create and modify a trigger.
Triggers can be enabled or disabled using ENABLE and DISABLE commands and
can be modified as (ALTER, DROP) as is done to tables.
318
SQL Star International Ltd.
Lab Exercises
1.
Create a procedure called prCheckDML that prevents any DML statement
from being executed outside normal office hours, Monday through Friday, and
returns a message saying You are allowed to make changes only during office
hours.
2.
Create a statement trigger trigSecureEmp on the Employees table that
invokes the procedure prCheckDML.
3.
Create a row trigger trigRestrictSal, which will allow a new row to be
inserted into the Employees table or salary to be updated only if the new
department ID happens to be 20 or 60, and new salary happens to be more than
$12,000. Your trigger code should display an appropriate message if the insert or
update on Employees table fails. For instance, on attempting to update the salary of
employee 101 (who does not belong to departments 20 or 60) to $14,000, the
trigger trigRestrictSal should fire, and the following message must be displayed:
UPDATE Employees
*
ERROR at line 1:
ORA-20001: You are not eligible to earn this amount
ORA-06512: at HR.TRIGRESTRICTSAL, line 4
ORA-04088: error during execution of trigger HR.TRIGRESTRICTSAL
[Note: For the purpose of question 3, you need to create a table AuditEmp, which
tracks a users activity on the Employees table.]
The structure of AuditEmp table should be as follows:
319
SQL Star International Ltd.
4.
Create a trigger called trigAuditEmp, which will record the values of columns
of Employees table (both before and after changes made) to the table AuditEmp.
Insert a new row into the Employees table, and update the salary and department
ID of employee 104. Query the AuditEmp table and view the result.
5.
320
SQL Star International Ltd.
Chapter 15
321
SQL Star International Ltd.
Objectives
In this chapter, you will be able to:
322
SQL Star International Ltd.
Triggers that fire due to DDL transactions are called DDL event triggers. You create
these triggers when using the CREATE, ALTER and DROP commands. One of the
reasons for creating these triggers could be the security of the database to prevent illegal
manipulation of database objects.
SCHEMA}
PL/SQL block;
Where,
ddl_event1 could be any of the statements CREATE, ALTER or DROP as and when
they are implemented on any of the database objects.
ON SCHEMA clause is specified if you want to create the trigger associated to some data
dictionary objects in your schema.
The rest of the syntax remains the same as that for creating DML triggers. An example
for this trigger is:
CREATE OR REPLACE TRIGGER trigPreventDrop
BEFORE DROP ON SCOTT.SCHEMA
BEGIN
IF DICTIONARY_OBJ_OWNER = SCOTT
AND DICTIONARY_OBJ_NAME LIKE MEM%
323
SQL Star International Ltd.
To test this trigger trigPreventDrop, first create a table Members, which is a duplicate
of the Member table:
CREATE TABLE MEMBERS
AS SELECT *
FROM MEMBER;
Table created.
Now, when you attempt to drop this newly created table, the DDL trigger prevents the
operation from succeeding and displays a message conveying the same:
DROP TABLE MEMBERS;
DROP TABLE SCOTT.MEMBERS
*
ERROR at line 1:
ORA-00604: error occurred at recursive SQL level 1
ORA-20001: YOU ARE NOT PERMITTED TO DROP THE TABLE
ORA-06512: at line 6
324
SQL Star International Ltd.
AFTER SERVERERROR trigger that will fire when an error is logged in the
database
BEFORE LOGOFF trigger, which fires when you try to log off from the
database
AFTER STARTUP trigger, which fires when you open the database
BEFORE SHUTDOWN trigger that fires whenever you shutdown the database
These triggers are created to track the number of times a user logs on and off from the
database. This trigger could be specified for a database or a schema. If for the former,
then the data you retrieve is the count of number of users accessing the database. If the
trigger is on a schema, then it keeps track of the number of time a users logs on and off
from his schema.
The following codes show you the use of log on and the log off triggers:
Note: For the following two examples, create a table LogTable, which should have the
following structure:
325
SQL Star International Ltd.
To test the trigger, log off the database and log in again.
Now, query the LogTable. The table displays the following result:
To test the trigger, log off the database and log in again.
Now, query the LogTable. The table displays the following result:
You have seen how triggers are created for DML, DDL and database events. There are a
few more operations that can be done with triggers. They include:
Using the CALL statement in triggers
You can call any existing stored procedure instead of coding a PL/SQL block for the
trigger to perform some actions. The procedure could be implemented on any
software platform like PL/SQL, Java or C.
You reference trigger attributes using this statement with the :NEW and :OLD
parameters.
An example for this trigger is:
CREATE OR REPLACE TRIGGER trigChkFine
AFTER UPDATE OF dActualReturnDt ON Transaction
326
SQL Star International Ltd.
LOGIN_USER: returns the name of the user who has logged in.
327
SQL Star International Ltd.
Managing Triggers
When creating triggers, you must manage them in a way to optimize their functionality.
Managing Trigger Development
The three types of DML triggering statements are INSERT, UPDATE and DELETE.
Based on when they fire, triggers are classified as BEFORE and AFTER. In addition,
they are also classified as row and statement level triggers. So taking a combinations of
these types, you can create 12 types of triggers for one table in an Oracle database.
There is no need to create 12 triggers for all tables, as there is in-built functionality to
handle various situations. You could have triggers on your table, which in turn may fire
other triggers. However, these should be designed carefully so that there is no excess
trigger processing that happens.
For example you can create an INSERT trigger, Trg1 on table Tab1. When this trigger
fires, it inserts a row into table Tab2. An insert into Tab2 causes another INSERT
trigger, Trg2, against it to fire. Trg2 in turn inserts a row into Tab1. This is a badly
designed set of triggers as it causes a loop of executions to form and as a result no
insertions happen.
When a trigger causes another trigger to fire and that in turn causes a third one to fire, it
is said to be cascading of triggers. A maximum of 32 cascading triggers is allowed to
fire. Setting the MAX_OPEN_CURSORS parameter when starting up the database can
restrict this figure.
Some restrictions when working with triggers are:
You can use a trigger to insert data into a column of LONG or LONG RAW
datatype, but you cannot make use of these datatypes to declare variables in
a trigger. To extract data from a column of this datatype, you have to convert
your trigger column to a VARCHAR2 datatype and then perform the select.
NEW and OLD references also cannot be made.
328
SQL Star International Ltd.
Triggers do not process rows according to an order you want. If you want to
specify the order of processing rows, then the trigger should include an
ORDER BY clause.
You need to explicitly handle exceptions in triggers. Triggers are the same as
any other PL/SQL block so they can also include an exception in their body.
The two basic rules that apply when using triggers are that:
Your trigger should not change data in the primary key, foreign key or unique
key columns of a constraining table.
You can understand the rules better, by understanding what mutating and constraining
tables are.
A constraining table is one that a trigger needs to access directly for DML statement or
indirectly when the SQL statement requires a foreign key check.
A mutating table is one that is either being modified by a DML statement, or needs to be
modified due to a DELETE CASCADE referential integrity action.
329
SQL Star International Ltd.
Database triggers are invoked implicitly and stored procedures are explicitly
invoked.
COMMIT, SAVEPOINT and ROLLBACK are not allowed in triggers but are
allowed in stored procedures.
Security
Auditing
Non-declarative integrity
Data integrity
Referential integrity
Table replication
Derived data
330
SQL Star International Ltd.
The server verifies the user name when a user connects to a database, but
with a trigger you can set privileges not only on the user, but also on
database values. These values could be the day of the week or time among
others.
Oracle server can determine who can access tables, views, synonyms and
sequences. A trigger can be created to specifically restrict access to tables
only.
Oracle server specifies privileges for data manipulation and data definition
changes. A trigger can be created to control data manipulations only.
(TO_CHAR (SYSDATE,
book issue
transactions
on
Sundays
and
beyond
library hours);
END IF;
END;
/
331
SQL Star International Ltd.
Trigger created.
Auditing
Within the server, you can monitor and gather data regarding specific database activities.
With triggers you can audit actual data values.
The auditing activities performed by the Oracle server and triggers include:
Oracle server writes the audit trail to the centralized audit table (that is, a
data dictionary table or an Operating System file), while a trigger can write it
to a user-defined audit table.
Oracle server generates an audit report for every one session or access
attempt, while a trigger can generate audit reports for every row.
332
SQL Star International Ltd.
With a trigger, you can provide variable default values, enforce dynamic constraints,
enable and disable dynamically, and incorporate declarative constraints within the
definition of a table.
Referential integrity is implemented to avoid data inconsistency. The important issues
are:
When implementing referential integrity with triggers, some actions not possible by
declarative constraints are taken care of:
Cascading triggers
333
SQL Star International Ltd.
Table Replication
The Oracle server replicates a table by creating snapshots. A snapshot is a local copy of a
table that originates from one or more remote master tables. You can read data from a
snapshot but not perform DML statements on it. Data in the snapshot is periodically
refreshed so that it shows the latest data as is in the master. Some activities you can
perform with snapshots are:
334
SQL Star International Ltd.
335
SQL Star International Ltd.
END;
The Oracle server allows you to compute derived values in a batch by:
Modifying the data in one step and calculating the derived value in the next
Modifying the data and performing the calculations are done in one step
336
SQL Star International Ltd.
Procedure code
CREATE OR REPLACE PROCEDURE prIncCopies
(vBookId IN Book.cBookID%TYPE, vCopies IN Book.nNoOfCopies%TYPE)
IS
BEGIN
UPDATE Book
SET nNoOfCopies = nNoOfCopies + vCopies
WHERE cBookID = vBookId;
END;
/
Procedure created.
Summary
337
SQL Star International Ltd.
338
SQL Star International Ltd.
Lab Exercises
1.
Write a trigger on your schema to track when you have logged in and logged out.
2.
What happens if a procedure that updates a column of table X is called in a
database trigger of the same table ?
339
SQL Star International Ltd.
340
SQL Star International Ltd.