PLSQL For Beginners - Training Guide
PLSQL For Beginners - Training Guide
Training Guide
PL/SQL
for
Beginners
Training Guide www.appltop.com
info@appltop.com
PL/SQL for Beginners
Introduction
4 - Exceptions 88
What is an Exception? 90
Types of Exception 91
Handling Exceptions 93
Exception Propagation 97
Creating Your Own Exceptions 99
Exceptions for an Oracle Error Code 100
Exceptions for your own Conditions 101
Summary 102
7 – Packages 153
What is a Package? 155
Packages and Scope 158
Creating and using a Package 159
Overloading Subprograms 166
Invoking Packaged Functions from SQL Statements 169
Privileges & Security 173
Oracle Provided Packages 176
Summary 177
8 – Triggers 179
What is a Trigger? 181
Trigger Types 183
Creating a Trigger 185
Restrictions on Triggers 190
Summary 191
BEGIN
v_number := 10;
END;
BEGIN
v_number := 10;
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('Error');
END;
BEGIN
v_number := 10;
Block 1
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('Error 1');
END;
BEGIN
v_number := 20;
Block 2
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('Error 2');
END;
BEGIN
v_number := 20;
Block 3
EXCEPTION
WHEN OTHERS THEN
DBMS_OUTPUT.put_line('Error 3');
END;
EXCEPTION
WHEN some_error THEN
BEGIN
Inner Block
. . . Error handling
EXCEPTION
. . .
END;
END
DECLARE
x NUMBER;
BEGIN
DECLARE
y NUMBER; Inner Block Outer Block
Scope of y Scope of x
BEGIN
. . .
END;
. . .
END;
BEGIN
DECLARE
x NUMBER; x from outer
block has no
BEGIN visibility
. . .
END;
. . .
END;
Comments
Comments can be added to your code in two ways:-
Single-line Comments
These comments start with two dashes, the
comment continues until the end of the line, for
example:-
-- Local declarations
v_number NUMBER ; -- A number variable
Multiple-line Comments
These comments begin with /* and end with */,
everything in between these delimiters is treated as
a comment, for example:-
/* Determine what the item
type is before continuing */
IF v_item_type = 'A' THEN
. . .
Comments
Comments should be used as much as possible.
Good comments can make your code much easier to
understand. Do not over-comment or include
comments for comments sake. Try not to simply
repeat what is already clear in your code, much of
PL/SQL is quite readable and comments such as:-
-- Test if v_number is equal to 10
IF v_number = 10 THEN
. . .
Comments
Different developers have different style for
comments, it's really down to personal choice but
here are a few pointers:-
• Always indent comments to the same level as
your code
• Choose a low maintenance style, for example,
High Maintenance
-- ----------------- --
-- This is a comment --
-- ----------------- --
Low Maintenance
--
-- This is a comment
--
• Don't restate the obvious
• Try to comment each logical block of code
• Try to maintain a modification history within the
code, either at the top or bottom of a program
• Remember, other developers may need to read
your comments
Variables
Variables are an essential part of any programming
language. A variable is simply a location in memory
where you can store data of different types.
Variables - Declaration
Before a variable can be used within your code, you
must first declare it. All declarations must appear in
the DECLARE section of your block. Variable
declaration has the following syntax:-
varname TYPE [CONSTANT] [NOT NULL] [:= value];
Variables - Initialisation
If you declare a variable without specifying a
default/initial value, you may be wondering what
value it does contain, PL/SQL automatically
initialises all variables without a default value to
NULL. The is unlike many other languages such as C
where a variable may just contain garbage once
declared.
Variables - Constants
A variable can be declared as a constant value by
using the CONSTANT keyword within the variable
declaration.
A constant allows you to assign a value to an
identifier that can then be referenced throughout
the program. Any attempt to change a constant
value will cause an error to occur.
Viewing Errors
In any errors are present in your program (compile
time errors), you can view errors by querying the
USER_ERRORS database view, a more convenient
method is to use the SQL*Plus command:-
SQL> show err[ors]
NOTE
Output is only given once the PL/SQL code has
finished.
Flow Control
PL/SQL provides many structures that control the
flow of a program. PL/SQL provides the following
conditional expressions:-
• IF statement
• EXIT statement
The following looping structures are also provided:-
• Simple LOOP
• FOR loop (Numeric FOR Loop)
• WHILE loop
• Loop Labels
• Block Labels
• The GOTO statement
• CURSOR FOR loop (covered later)
Flow control structures are an essential part of any
third generation programming language, it is these
constructs combined with variables and
subprograms that give PL/SQL its power over SQL.
We will now take a look at each of the flow control
statements mentioned above.
Flow Control
The IF Statement
The IF statement allows PL/SQL to take different
actions based on certain conditions, its syntax is
very similar to the IF statement found in many
other programming languages.
Condition
We have covered conditions already but just to
recap, a condition is the result of a Boolean
expression, for example, the condition:-
X > Y
Action
An action is one or more PL/SQL statements. An
action can be anything from inserting into a table to
performing more IF statements.
Flow Control
The IF Statement
The IF statement has the following syntax:-
IF condition1 THEN
action1;
[ELIF condition2 THEN
action2;]
[ELSE
action3;]
END IF;
For example,
DECLARE
l_x NUMBER := 10;
l_y NUMBER := 20;
BEGIN
IF l_x > l_y THEN
DBMS_OUTPUT.put_line('X is greater than Y');
ELIF l_x < l_y THEN
DBMS_OUTPUT.put_line(X is less than Y');
ELSE
DBMS_OUTPUT.put_line('X is equal to Y')
END IF;
END;
Flow Control
The IF Statement
Here is a flowchart of the previous example:-
Flow Control
Simple Loop
A loop is a section of code that needs to be
executed a number of times. PL/SQL has many
kinds of loops, the most basic form of loop, a simple
loop has the following syntax:-
LOOP
statements ..
END LOOP;
Flow Control
Simple Loop
The EXIT statement can also be used inside an IF
statement:-
LOOP
l_x := l_x + 10;
Flow Control
The FOR Loop
A FOR (numeric FOR) loop is similar to the simple
loop we have already seen, the main difference is
the controlling condition is found at the start of the
loop and the number of iterations is known in
advance, whereas with a simple loop the number of
iterations is not known and is determined by the
loop condition (the EXIT statement). The FOR loop
has the following syntax:-
FOR variable IN [REVERSE] start_value .. end_value
LOOP
statements ..
END LOOP;
Flow Control
The FOR Loop
Here are a few examples of using a numeric FOR
loop.
To display numbers 1 to 10:-
FOR counter IN 1 .. 10
LOOP
DBMS_OUTPUT.put_line(counter);
END LOOP;
Flow Control
The WHILE Loop
The WHILE loop again is very similar to the simple
loop, even more so than the FOR loop. A WHILE
loop will continue so long as a condition is true. Its
syntax is as follows:-
WHILE condition
LOOP
statements . .
END LOOP;
Flow Control
Nested Loops
Loops can be nested to almost any number of
levels. You can nest different kinds of loops within
other types, for example, you may have a numeric
FOR loop which is nested within a WHILE loop, for
example:-
WHILE l_continue
LOOP
Inner Loop
Outer Loop
FOR count IN 20..29
LOOP
l_total := count + l_x;
END LOOP;
Flow Control
Labelling Loops
Let's say you have written some code which makes
use of nested loops, how do you make the program
terminate an outer loop from an inner loop?
This can be done is two ways:-
• Raise an exception, we cover exceptions later
• Label the loop and use the EXIT statement, we
cover this method next.
Flow Control
Labelling Loops
You can give loops a label, you do this by prefixing
the loop with the following:-
<< label-name >>
for example,
<<my_loop>>
WHILE l_continue
LOOP
. . . .
END LOOP my_loop;
<<inner>>
FOR count IN 20..29
LOOP
l_total := count + l_x;
Flow Control
Labelling Blocks
You can label a block in the same way you can label
a loop, for example:-
<<block1>>
DECLARE
l_x NUMBER;
BEGIN
l_x := 10;
Block 1
<<block2>>
DECLARE
Block 2
l_x NUMBER;
BEGIN
l_x := 20;
END block2;
DBMS_OUTPUT.put_line(l_x);
END block1;
<<block2>>
DECLARE
l_x NUMBER;
BEGIN
block1.l_x := 20;
END block2;
DBMS_OUTPUT.put_line(l_x);
END block1;
Flow Control
The GOTO Statement
Using labels, you can use the GOTO statement.
GOTO allows you to directly jump (branch) to a
particular section of code. GOTO is unconditional and
final, there is no return. Here is an example:-
DECLARE
l_x NUMBER := 30;
l_y NUMBER := 20;
BEGIN
IF l_x >= l_y THEN
GOTO skip_calc;
END IF;
l_x := l_y;
DBMS_OUTPUT.put_line('l_x is now same as l_y');
<<skip_calc>>
DBMS_OUTPUT.put_line(l_x);
END;
Summary
Phew!!! That was a lot of information to take in. To
summarise then, we have learnt:-
• Basic syntax of PL/SQL
• The PL/SQL block
• Multiple Blocks
• Nested Blocks
• Block Types - Anonymous, Named, Subprograms
and Triggers
• Scope and Visibility
• Comments - Single and Multiple line
• Variables - Scalar types, Declaration, Initialisation,
Type Anchoring
• Constants
• Assignments and Expressions
• PL/SQL from SQL*Plus
• Viewing the Results
• Flow Control - Branching, Looping, Nested Loops,
Labelling Loops, Labelling Blocks and GOTO
• PL/SQL coding Style
Now lets test our knowledge!!!
What is Next?
Section 3 deals with using SQL from with PL/SQL.
We look at constructing a SELECT statement and
using it within PL/SQL, we will also look at
transaction control and explain what an Implicit
Cursor is?
Section Three
SQL within PL/SQL
NOTE
Oracle provides a package called DBMS_SQL which
allows you to create dynamic SQL and PL/SQL,
using this package it is possible to perform DDL
within PL/SQL. This is quite an advanced topic and
is not covered on this course.
DECLARE
l_emp_name emp.ename%TYPE;
BEGIN
SELECT ename
INTO l_emp_name
FROM emp
WHERE sal = (SELECT MAX(sal) FROM emp);
DBMS_OUTPUT.put_line(l_emp_name);
END;
BEGIN
-- Clear down debug table
DELETE debug;
END;
Transaction Control
The COMMIT and ROLLBACK statements control
transactions. A transaction is not determined by a
block. For example:-
BEGIN
UPDATE emp
SET sal = sal * 1.15
WHERE TRUNC(hiredate) <
TRUNC(ADD_MONTHS(SYSDATE,-36));
COMMIT;
UPDATE emp
SET sal = sal * 1.10
WHERE TRUNC(hiredate) <
TRUNC(ADD_MONTHS(SYSDATE,-24));
COMMIT;
END;
Transaction Control
You can also make use of the SAVEPOINT
statement within PL/SQL, for example:-
BEGIN
DELETE debug;
SAVEPOINT deleted_debug;
DELETE transactions;
ROLLBACK TO deleted_debug;
COMMIT;
END;
IF SQL%NOTFOUND THEN
DBMS_OUTPUT.put_line('No rows updated');
ELSE
l_rows_updated := SQL%ROWCOUNT;
DBMS_OUTPUT.put_line('Updated :'||
TO_CHAR(l_rows_updated));
END IF;
END;
Summary
What is Next?
In section 4 we look at exceptions. We describe
what an exception is and how they are handled. We
also take a quick look at user-defined exceptions.
Section Four
Exceptions
Exceptions
This section deals with exceptions. We will be
covering the following:-
• What is an Exception?
• Types of Exception
• Handling Exceptions
• Exception Propagation
• Creating your own Exceptions
At the end of this section there will be several self-
test questions to answer and exercises to perform
What is an Exception?
An Exception is an identifier in PL/SQL that is used
to trap for an abnormal condition. During the
execution of a block, if an error occurs then an
exception is raised, the block will terminate and
control will pass to the exception handler if present.
Types of Exception
There are two types of exception:-
• Pre-defined - PL/SQL comes with several
predefined exceptions, these are directly
associated with Oracle error codes.
• User-Defined - These are declared by the
programmer and can be associated with an
Oracle error code that has no exception or they
can be wholly your own error condition.
Types of Exception
PL/SQL comes with many pre-defined exceptions,
these are directly associated with an Oracle error
code. The exception provides a much easier way to
trap for certain conditions, i.e. trapping for
NO_DATA_FOUND is easier and more intuitive that
checking for Oracle error code -1403. Here is a list
of the more common exceptions:-
Exception Oracle Error Occurs when …
DUP_VAL_ON_INDEX -1 Attempt to store a
duplicate key
NO_DATA_FOUND -1403 SELECT statement
returns no rows
VALUE_ERROR -6502 Arithmetic,
truncation,
conversion error
INVALID_CURSOR -1001 Invalid cursor
operation such as
closing an already
closed cursor
TOO_MANY_ROWS -1422 If a SELECT
statement returns
more than one row
INVALID_NUMBER -1722 Attempt to convert a
non-numeric value to
a numeric
Handling Exceptions
Exceptions are handled through the use of
Exception Handlers. An Exception handler is a
section of PL/SQL code found at the end of a block
labelled EXCEPTION. Within this section of code you
use the WHEN statement to handle specific
exceptions. For example:-
DECLARE
l_string VARCHAR2(2);
BEGIN
l_string := 'ABC';
END;
Handling Exceptions
A much better way to handle this problem is by
using an Exception Handler, so, using the previous
example but with an Exception Handler this time:-
DECLARE
l_string VARCHAR2(2);
BEGIN
l_string := 'ABC';
EXCEPTION
WHEN value_error THEN
DBMS_OUTPUT.put_line
('Could not store value');
END;
Handling Exceptions
There is a special exception handler called OTHERS,
this can be used with the WHEN statement to trap
ALL exceptions not already catered for. For
example:-
DECLARE
l_name emp.ename%TYPE;
BEGIN
SELECT ename
INTO l_name
FROM emp
WHERE empno = l_empno;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.put_line
('Could not find employee');
WHEN OTHERS THEN
DBMS_OUTPUT.put_line
('A Fatal Error Occurred');
END;
Handling Exceptions
There are two very useful functions that can be
used in conjunction with Exception Handlers:-
• SQLCODE - Returns the error number associated
with the exception.
• SQLERRM - Returns the complete error message
for the exception, including the error code.
Putting these to use in the previous example:-
DECLARE
l_name emp.ename%TYPE;
BEGIN
SELECT ename
INTO l_name
FROM emp
WHERE empno = l_empno;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.put_line
('Could not find employee');
WHEN OTHERS THEN
IF SQLCODE = -1422 THEN
DBMS_OUTPUT.put_line
('Too many rows returned');
ELSE
DBMS_OUTPUT.put_line
('Fatal Error : '||SQLERRM);
END IF;
END;
Exception Propagation
Exception Propagation is the way exceptions are
passed to the enclosing block if they are left un-
handled. For example, if an error occurs in the
following code within the inner block, then the
exception is passed to the enclosing block where it
is then handled:-
DECLARE
l_string1 VARCHAR2(3);
BEGIN
l_string1 := 'ABC';
DECLARE
l_string2 VARCHAR2(3);
BEGIN
l_string2 := 'ABCD';
Exception raised here and
END; passed to enclosing block
where it is handled
EXCEPTION
WHEN value_error THEN
DBMS_OUTPUT.put_line('Error');
END;
Be aware that if an exception is not handled by any
handler then the exception will propagate out to the
host environment, thus completely stopping
execution of your PL/SQL program.
Exception Propagation
You may want certain actions to be performed if an
error occurs but you may still want the program to
continue, you do this by enclosing your code in its
own block with its own exception handler, if the
statement fails, you catch the exception and then
continue with the rest of the program as normal, for
example:-
DECLARE
l_string VARCHAR2(3);
BEGIN
l_string := 'ABC'; Exception raised here
BEGIN
l_string := 'ABCD';
EXCEPTION
Handled here
WHEN VALUE_ERROR THEN
DBMS_OUTPUT.put_line
('Could not store value');
END;
Execution continues here
l_string := 'DE';
END;
Summary
We have seen that Exceptions are basically PL/SQL's
method of handling errors that occur during
execution of a block.
• There a two types of Exception, pre-defined and
user-defined
• Exceptions are handled with exception handlers
within your code.
• Exceptions always propagate to the enclosing
block if left un-handled.
• You can create your own exceptions for Oracle
Error codes and your own error conditions.
What is Next?
In section 5 we will cover explicit Cursors, we will
describe what an explicit cursor is and how to use
them.
Section Five
Explicit Cursors
Explicit Cursors
In this section we will learn about Explicit Cursors,
we will cover:-
• What is an Explicit Cursor?
• How to use an Explicit Cursor . . .
• Opening
• Fetching From
• Closing
• Cursor Attributes
• A Complete Example
• WHERE CURRENT OF clause
• Cursor FOR Loops
OPEN employees_cur;
l_empno emp.empno%TYPE;
l_ename emp.ename%TYPE;
BEGIN
OPEN employees_cur('CLERK');
FETCH employees_cur
INTO l_empno,l_ename;
. . .
r_emp_rec employees_cur%ROWTYPE;
BEGIN
OPEN employees_cur('CLERK');
The above code will return all rows in the active set,
then exit the loop the first time the FETCH returns
no data.
LOOP
-- Retrieve row from active set
FETCH employee_cur INTO r_emp_rec;
CLOSE employee_cur;
END;
UPDATE emp
SET sal = sal * 1.15
WHERE CURRENT OF employee_cur;
CLOSE employee_cur;
END;
. . .
r_emp_rec employees_cur%ROWTYPE;
BEGIN
OPEN employee_cur('CLERK');
LOOP
FETCH employee_cur INTO r_emp_rec;
<process data here>
EXIT WHEN employee_cur%NOTFOUND;
END LOOP;
CLOSE employee_cur;
END;
Summary
• Explicit Cursors are named PL/SQL constructs for
querying multiple rows
• You use the OPEN statement to open an explicit
cursor
• Rows are retrieved with the FETCH statement
• The CLOSE statement closes the cursor
• Cursor Attributes - ROWCOUNT, FOUND,
NOTFOUND and ISOPEN
• WHERE CURRENT OF clause
• Cursor FOR Loops - Using declared cursors and
SELECT sub-statements
What is Next?
The next section deals with Stored Procedures and
Functions. All the code we have seen so far have
been in the form of an Anonymous Block, this is an
unnamed block of PL/SQL that you execute by
providing SQL*Plus with the actual filename of the
code, Stored Procedures and Functions are blocks of
code which are stored in the database and can be
executed from any other PL/SQL block.
Section Six
Stored Procedures &
Functions
Client
Server
Oracle Server
Storage &
Execution of
Stored
Procedures &
Functions
r_emp_info emp_info_cur%ROWTYPE;
BEGIN
OPEN emp_info_cur(p_empno);
CLOSE emp_info_cur;
p_ename := r_emp_info.ename;
p_job := r_emp_info.job;
p_deptno := r_emp_info.deptno;
END;
BEGIN
EmpInfo( 7902
, l_ename
, l_job
, l_deptno);
DBMS_OUTPUT.put_line(l_ename);
DBMS_OUTPUT.put_line(l_job);
DBMS_OUTPUT.put_line(l_deptno);
END;
[EXCEPTION
<exception handlers>]
END;
r_empname empname_cur%ROWTYPE;
BEGIN
OPEN empname_cur(p_empno);
IF empname_cur%NOTFOUND THEN
r_empname.ename := 'UNKNOWN EMPLOYEE';
END IF;
CLOSE empname_cur;
RETURN r_empname.ename;
END;
BEGIN
l_employee_name := GetName(7902);
DBMS_OUTPUT.put_line(l_employee_name);
END;
Handling Exceptions
The way you handle errors that occur in your code
is pretty much down to you, personal style or
company/client guidelines may dictate your method.
In any case, here are a few pointers for Exception
Handling within stored subprograms:-
• Make your function or procedure as robust as
possible, i.e. error trap anything that can go
wrong - because it probably will!!
• Make use of the EXCEPTION section in your
code, handle local exceptions in here and pass
the exception on to the calling program using a
statement called RAISE_APPLICATION_ERROR,
see next slide for an example.
• If a procedure can either work or fail then try to
convert it into a function with a BOOLEAN return
value, this should be TRUE if the function worked
okay, otherwise FALSE.
• You can use OUT or IN OUT parameters in
procedures and function to pass back status
information.
Handling Exceptions
RAISE_APPLICATION_ERROR
This statement can be used to pass error
information back to a calling program, it takes the
following syntax:-
RAISE_APPLICATION_ERROR(error_number,error_text);
Handling Exceptions
RAISE_APPLICATION_ERROR
Here is an example:-
CREATE OR REPLACE FUNCTION balance
(p_account IN NUMBER)
RETURN NUMBER
IS
l_balance NUMBER;
BEGIN
SELECT balance
INTO l_balance
FROM bank_account
WHERE account_no = p_account;
RETURN l_balance;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE_APPLICATION_ERROR(-20500
, 'Unknown Account Number');
END;
Handling Exceptions
RAISE_APPLICATION_ERROR
Given the function balance, here is how you could
catch the error it returns:-
DECLARE
l_account_balance NUMBER;
e_unknown_account EXCEPTION;
PRAGMA EXCEPTION_INIT(e_unknown_account,-20500);
BEGIN
l_account_balance := balance(14956);
EXCEPTION
END;
Local Subprograms
Stored subprograms are stored within the database
itself. Another type of subprogram is one that is
local only to a block of PL/SQL, these subprograms
are defined in the declaration section of another
block of code. They are generally created for helper
routines, i.e. routines that are useful only to the
program they defined in and therefore should not be
available to anyone else. Local subprograms are
defined in almost the same way as a stored
subprogram, the only difference being the you do
not prefix the FUNCTION or PROCEDURE statement
with CREATE OR REPLACE. The definition of a
local subprogram should be in the declarative
section of a PL/SQL block, after any other
declarations (such as variables, constants,
cursors,..etc)
Local Subprograms
Here is a common example of the local
subprogram:-
DECLARE
l_account_balance NUMBER;
e_unknown_account EXCEPTION;
PRAGMA EXCEPTION_INIT(e_unknown_account,-20500);
BEGIN
l_account_balance := balance(14956);
p('Balance = '||TO_CHAR(l_account_balance));
EXCEPTION
END;
A Few Guidelines
Here are a few guidelines for developing stored
subprograms:-
• Generic - if possible, try to make your
subprogram as generic as possible, this way, it is
likely to be re-used more often
• Robust - make your subprogram as robust as
possible, trap anything that can go wrong
• One Action - try to ensure you subprogram only
performs one action, by this I mean one logical
action, if more than one action is needed then
more than one subprogram is needed
• Local subprograms - Make use of local
subprograms, they can make your code easier to
read and debug, though be careful, overuse can
have the opposite effect
• Arguments/Parameters - many arguments can
make your subprogram flexible but also make it
hard to use, try to derive as many values as you
can from other values
Summary
Stored subprograms, in particular Procedures and
Functions are very powerful constructs.
• Stored and executed in the database
• Procedures are PL/SQL statements
• Functions are RVALUES's that can be used in
expressions
• Functions can also be invoked from a SQL
statement
• Error handling with
RAISE_APPLICATION_ERROR
• Subprograms can be local to a block
What is Next?
In the next section we take a look at Packages.
Packages are one of PL/SQL's most powerful
features. They allow you to group together,
variables, cursors, functions and procedures into
one object.
Section Seven
Packages
Packages
This section deals with one of PL/SQL's most
powerful features, the Package. In particular, we
cover:-
• What is a Package?
• Packages and Scope
• Creating a Package
• Overloading
• Invoking Functions from SQL Statements
• Privileges & Security
• Oracle supplied Packages
What is a Package?
A Package is another type of named PL/SQL block. A
Package allows you to create related objects
together. A Package is stored in the database as
with Stored Procedures and Functions, though a
Package cannot be local to a block. A Package has
two parts, a Specification (or header) and a Body,
each part is stored separately within the database.
The Specification of a Package contains details of
what can be found in the Package, though it does
not contain any of the code that implements the
Package, this code is stored within the Package
Body.
What is a Package?
Objects declared in the specification are called
public objects, that is, they can be seen and used by
anyone with the correct privileges.
What is a Package?
A typical package will consist of many public
functions and procedures, these are declared in the
specification and defined in the body. A typical
package may also contain many private variables,
cursors, exceptions, functions and procedures, all
defined in the body. We will look at creating a
typical package over next few slides.
A Typical Package
Specification
Function A Declaration
Package
Private Variable
Private Variable
Function A Definition
Procedure C Definition
Function E Definition
Creating a Package
Before we write any code, we need to define what
our package actually does; here is a description for
our package:-
• Maintain a bank balance
• Make withdrawals (reduce balance)
• Make deposits (increase balance)
• Query balance
Also, we can define some business rules at this
point:-
• Balance cannot be overdrawn
• There should be some kind of limit to how much
can be withdrawn per transaction.
Creating a Package
First, you need to create the package specification,
this is done with the following statement:-
CREATE OR REPLACE PACKAGE package_name
IS|AS
<package-specification>
END [package name]
Creating a Package
Looking at the description for our bank account
package, we can see that:-
• We require 3 public functions or procedures:-
1. Withdrawal
2. Deposit
3. Balance query
• We require private data
1. Current balance
2. Maximum allowed per withdrawal
Creating a Package
Now, let's create our package specification:-
CREATE OR REPLACE PACKAGE bank_account
IS
-- Procedure for making a withdrawal
PROCEDURE withdraw(p_amount IN NUMBER);
Creating a Package
We now need to create the package body, this is
where the actual code appears for all public
subprograms as well as any other private objects.
You create the body with the following statement:-
CREATE OR REPLACE PACKAGE BODY package_name
IS|AS
<package-body>
END [package name]
Creating a Package
The following code will create our package body:-
CREATE OR REPLACE PACKAGE BODY bank_account
IS
--
-- Private data objects
--
-- Hold account balance, initialised to 0
v_balance NUMBER := 0;
--
-- Private functions/procedures
--
-- Print text to screen
PROCEDURE p(p_text IN VARCHAR2)
IS
BEGIN
DBMS_OUTPUT.put_line(p_text);
END;
--
-- Public function/procedure definitions
--
-- Procedure for making a withdrawal
PROCEDURE withdraw(p_amount IN NUMBER)
IS
l_new_balance NUMBER:= v_balance - p_amount;
BEGIN
IF p_amount > l_max_withdrawal THEN
-- Ensure amount is within limit
p('Withdrawals limited to '||
TO_CHAR(l_max_withdrawl)||
' per transaction');
-- No overdrafts allowed
ELSIF l_new_balance < 0 THEN
-- No overdrafts allowed
p('No overdraft available');
p('Cash available : '
||TO_CHAR(v_balance));
ELSE
v_balance := v_balance - p_amount;
p('Here is your cash!');
END IF;
END withdraw;
continued . . .
Creating a Package
-- Procedure for making a deposit
PROCEDURE deposit(p_amount IN NUMBER)
IS
BEGIN
IF p_amount <= 0 THEN
p('Deposit must be at least £1');
ELSE
v_balance := v_balance + p_amount;
p('Thankyou!');
END IF;
END;
BANK_ACCOUNT.withdraw(100);
BANK_ACCOUNT.deposit(500);
DBMS_OUTPUT.put_line(BANK_ACCOUNT.balance);
BANK_ACCOUNT.withdraw(275);
DBMS_OUTPUT.put_line(BANK_ACCOUNT.balance);
END;
Overloading Subprograms
Overloading is method of creating more than one
subprogram (function or procedure) with the same
name but with different arguments. This allows you
to create very useful subprograms that can act in
different ways depending on what data they are
given. Oracle comes with many overload
subprograms, take the built-in function TO_CHAR,
this function is overload even though it appears to
be a single function whenever you use it, have a
look at the function declarations below:-
TO_CHAR(number)
TO_CHAR(number,format)
TO_CHAR(date)
TO_CHAR(date,formart)
Overloading Subprograms
How might we use subprogram overloading in our
bank account package?
Lets say we want to add functionality that allows the
user to withdraw all remaining funds, we could do
this in a number of ways:-
1. Get current balance and withdraw that amount
2. Change the withdraw procedure to add a flag
which indicates you want all the cash
3. Create a new procedure to withdraw all the cash
All of the above are very possible (and easy in this
case) but a much better method would be to
overload the withdraw procedure; change it so
that if no arguments are provided then withdraw all
current funds.
Overloading Subprograms
Our specification would now contain two
declarations of withdraw:-
PROCEDURE withdraw;
PROCEDURE withdraw(p_amount IN NUMBER);
or even . . .
PROCEDURE withdraw
IS
withdraw(balance);
END;
Summary
As we have seen, the Package is a very powerful
construct.
• A Package is a construct used for grouping
related data and functions/procedures
• A Package consists of a specification and a body
• Public object declaration in specification
• Private and public object definition in body
• Packages are created with the CREATE PACKAGE
statement
• Function/Procedure Overloading
• Packaged Functions can be invoked from with
SQL Statements though with some restrictions
• Oracle provides many pre-built packages
What is Next?
The next and final section deals with the remaining
type of named subprogram, the database Trigger. A
database Trigger is a PL/SQL block that is implicitly
executed when a particular event in the database
occurs.
Section Eight
Triggers
Triggers
We now come to the remaining type of named
subprogram, the database Trigger. In this section
we cover:-
• What is a Trigger?
• Trigger types
• Creating a Trigger
• Restrictions on Triggers
This course is a beginners guide to PL/SQL and
therefore Triggers (as with all other subjects) are
covered very briefly. The following notes should be
enough to get you started.
What is a Trigger?
A trigger is a block of PL/SQL which is implicitly
invoked (fired) whenever a particular event occurs
in the database.
What is a Trigger?
Triggers have many possible uses, they could for
example, be used for:-
• Auditing System - because triggers are
guaranteed to fire, they are an ideal method of
auditing changes to a table, for example, you
could audit/log changes to a customer record.
• Automatically signal other programs that they
need to perform some action
• Archiving System - Archiving data can be
achieved very easily with triggers
• Maintaining Derived Values - Derived values can
be maintained with ease, for example, a stock
system need never actually change the stock
quantity, a trigger on the transaction table could
maintain it for you
• Complex Integrity Constraints - You may have
some complex integrity constraints that cannot be
implemented as declarative constraints on the
table
Trigger Types
The trigger type determines when the trigger
actually fires. There are 12 trigger types in all, these
types fall into one of two categories, the type
category:-
• Statement - These triggers fire in response to a
DML statement being issued
• Row - These triggers fire for all rows affected by
a DML statement. These are identified by the FOR
EACH ROW clause in the trigger definition.
Each of the above types has a further six types,
these belong, again, to one of two categories, the
timing category:-
• BEFORE - These triggers fire before the
statement is executed
• AFTER - These fire after the statement has
executed
Now the event category:-
• INSERT, UPDATE & DELETE - This determines
the kind of DML statement that fires the trigger.
Creating a Trigger
Triggers are created with the CREATE TRIGGER
statement, it has the following syntax:-
CREATE [OR REPLACE] TRIGGER trigger_name
BEFORE|AFTER trigger_event [OR trigger_event ...]
[OF column_list ] ON table
[FOR EACH ROW]
[WHEN (trigger condition)]
BEGIN
<trigger body>
END;
Trigger Name
This can be any legal identifier, again, as with all
objects, it is a good idea the make the name
meaningful, so for example, a trigger to audit an
items price might be called, audit_item_price.
Trigger Event
This is the keyword INSERT, UPDATE or DELETE.
Multiple events can be specified using the OR
keyword, such as INSERT OR UPDATE.
Creating a Trigger
Column List
This specifies the columns you want checking before
the trigger fires, the correct event may have
occurred but if the column list does not include a
column that was changed, the trigger will not fire.
Table
This specifies the triggering table.
FOR EACH ROW Clause
If present, then the trigger will file for each row
affected by the DML statement.
Trigger Condition
This acts as a condition that can be applied to
determine whether the trigger should fire or not.
You add a condition with the WHEN clause, the
condition itself can be practically anything that
would normally appear in an IF statement. Only
valid in a ROW level trigger. Enclose the whole
condition is parentheses.
Trigger Body
This is the actual code of the trigger. It is usual for
this simply to contain a stored procedure call.
Creating a Trigger
As an example, we shall create a trigger that
maintains a stock record based on a transactions
table. The definition of our trigger is:-
• We have a transaction table that records issues
and receipts
• We have a stock table that contains data relating
to current stock figures
• We want to automatically maintain the current
stock quantity based on ONLY transactions of
type ISS (issue) and RCT (receipt).
Creating a Trigger
To create a trigger to accomplish the above, the
following code is needed:-
CREATE OR REPLACE TRIGGER stock_quantity
BEFORE INSERT
ON transactions
FOR EACH ROW
WHEN new.transaction_type IN ('ISS','RCT')
DECLARE
l_use_quantity stock.quantity%TYPE;
BEGIN
IF :new.transaction_type = 'ISS' THEN
l_use_quantity := :new.quantity * -1;
ELSE
l_use_quantity := :new_quantity;
END IF;
UPDATE stock
SET quantity = quantity + l_use_quantity
WHERE item = :new.item;
END;
Creating a Trigger
You can reference values on the triggering table
during execution of the trigger with the new and
old keywords.
The new keyword is used to reference the value of
columns on a new or updated row, whereas the old
keyword is used to reference the value of a column
on a row being deleted or updated. See the
following table:-
Operation OLD Value NEW Value
INSERT NULL the new inserted
value
UPDATE the column value the column value
before the update after the update
DELETE the column value NULL
before deletion
Creating a Trigger
You may want to combine trigger events into one
trigger, i.e. your trigger may fire on INSERT OR
DELETE, but you may still want to take different
actions depending on the actual triggering event.
You can do this by using the INSERTING,
UPDATING and DELETING functions.
These functions simply return TRUE or FALSE
depending on what the trigger event was. Here is
an example:-
CREATE OR REPLACE TRIGGER audit
BEFORE INSERT OR UPDATE OR DELETE on items
FOR EACH ROW
BEGIN
IF INSERTING THEN
INSERT INTO audit_log('Row Inserted');
ELSIF UPDATING THEN
INSERT INTO audit_log('Row Updated);
ELSIF DELETING
INSERT INTO audit_log('Row Deleted);
END IF;
END;
The above trigger will simply insert a row into the
audit_log table, the text inserted depends on
what the trigger event is.
Restrictions on Triggers
There are a number of restrictions placed on the
code that is placed within a trigger body, these
restrictions also apply to any stored subprogram
invoked from the trigger.
• You CANNOT use COMMIT, SAVEPOINT and
ROLLBACK statements.
• Do not change data in the primary key, foreign
key or unique key columns of a constraining
table including the triggering table. A
constraining table is a table that may need to be
queried for referential integrity purposes. This
can create what is called a mutating table. Solving
Summary
• Triggers are named PL/SQL blocks and have
many possible uses
• Triggers fire when an event on the database
occurs
• There are 12 trigger types, Row level, Statement
Level, BEFORE and AFTER, INSERT, UPDATE and
DELETE
• Create triggers with the CREATE TRIGGER
statement
• Restrict firing with WHEN clause
• Reference triggering table columns with :old
and :new
• Using INSERTING, UPDATING and DELETING
functions when combining trigger events
• Be aware of restrictions and possible performance
issues
What is Next?
We have reached the end of the course, if you
followed all sections and attempted the exercises,
you should be able to produce your own PL/SQL
programs. You should also be able to support some
existing PL/SQL programs, in that you will be able to
read and understand most of the code.
We have covered a great deal of topics in a very
short space of time so do not be too concerned if
you haven't quite taken it all in, as said throughout
the course, it is a beginners guide, it is meant to
equip you with enough knowledge to at least get
you started on the road to becoming a PL/SQL
Developer.