PL/SQL
Introduction to PL/SQL
Procedural Language extension for SQL
Oracle Proprietary
3GL Capabilities
Integration of SQL
Portable within Oracle data bases
Callable from any client
When is PL/SQL handy
When something is too complicated for SQL
When conditional branching and looping are needed
Example
Write a PL/SQL program that assigns email address to each employee or
student in a table. Following these rules:
-
email address should be all lower case
email address should default to first initial plus the first seven letters of the last name
email can be no longer than eight characters
if email is already used than use first initial plus middle initial plus first
six letters of last name
- if the previous address is taken use the first two letters of the first name
and the first six letters of the last name.
- if the previous address is taken use first six letters of last name + 01 or 02 etc
PL/SQL BLOCK STRUCTURE
PL/SQL code is built of Blocks, with a unique
structure.
DECLARE (optional)
- variable declarations
BEGIN (required)
- SQL statements
- PL/SQL statements or sub-blocks
EXCEPTION (optional)
- actions to perform when errors occur
END; (required)
PL/SQL Block Types
Anonymous
DECLARE
BEGIN
-statements
EXCEPTION
END;
Procedure
PROCEDURE <name>
IS
BEGIN
-statements
EXCEPTION
END;
Function
FUNCTION <name>
RETURN <datatype>
IS
BEGIN
-statements
EXCEPTION
END;
PL/SQL Variable Types
Scalar (char, varchar2, number, date, etc)
Composite (%rowtype)
Reference (pointers)
LOB (large objects)
Note: Non PL/SQL variables include bind variables,
host (global) variables, and parameters.
Scalar Variables
Reference single value such as number, date, string
Data types correspond to Oracle 10g database data types
VARCHAR2
CHAR
DATE
NUMBER
PL/SQL has other data types that do not correspond to database
data types
Variable Naming Conventions
Two variables can have the same name if
they are in different blocks (bad idea)
The variable name should not be the same
as any table column names used in the
block.
PL/SQL Variable Constraints
NOT NULL
Can not be empty
CONSTANT
Can not be changed
PL/SQL is strongly typed
All variables must be declared before their
use.
The assignment statement
:=
is not the same as the equality operator
=
All statements end with a ;
PL/SQL Variables Examples
Age number;
Last char ( 10 );
DVal Date := Sysdate;
SID number not null;
Adjust constant number := 1;
CanLoop boolean := true
Other PL/SQL Data Types
BINARY_INTEGER more efficient than number
RAW ( max_length )
BOOLEAN (true, false, null)
Also LONG, LONG RAW and LOB types but the
capacity is usually less in PL/SQL than SQL
Composite Variables
Data object made up of multiple individual data
elements
Data structure contains multiple scalar variables
Composite variable data types include:
A
R
R
A
Y
RECORD (multiple scalar values similar to a tables record)
TABLE (tabular structure with multiple columns and
rows)
VARRAY (variable-sized array. Tabular structure
that can expand or contract based on data values)
13
Reference Variables
Directly reference specific database column or row
Assume data type of associated column or row
%TYPE data declaration syntax:
variable_name tablename.fieldname%TYPE;
%ROWTYPE data declaration syntax:
variable_name tablename%ROWTYPE;
LOB Data Type
Must be manipulated using programs in DBMS_LOB
14
package
DECLARE
Syntax
identifier [CONSTANT] datatype [NOT
NULL][:= | DEFAULT expr];
Examples
Declare
birthday
DATE;
age
NUMBER(2) NOT NULL := 27;
name VARCHAR2(13) := 'Levi';
magic CONSTANT NUMBER := 77;
valid BOOLEAN NOT NULL := TRUE;
sname Sailors.sname%TYPE;
reserves_record Reserves%ROWTYPE;
Printing Output
You need to use a function in the DBMS_OUTPUT
package in order to print to the output
If you want to see the output on the screen, you must type
the following (before starting):
set serveroutput on format wrapped size 1000000
Then print using
dbms_output. put_line(your_string);
dbms_output.put(your_string);
PL/SQL Sample Program
Variable g_inv_value number
DECLARE
v_price
number(8,2) := 10.25;
v_quantity
number(8,0) := 400;
BEGIN
:g_inv_value := v_price * v_quantity;
END;
/
Print g_inv_value
/
PL/SQL Sample Program
Set serveroutput on
DECLARE
v_inv_value number(10,2);
v_price
number(8,2) := 10.25;
v_quantity
number(8,0) := 400;
BEGIN
v_inv_value := v_price * v_quantity;
dbms_output.put('The value is: ');
dbms_output.put_line(v_inv_value);
END;
/
PL/SQL Sample Program
(with user input)
Set serveroutput on
Accept p_price Prompt 'Enter the Price: '
DECLARE
v_inv_value number(8,2);
v_price
number(8,2);
v_quantity
number(8,0) := 400;
BEGIN
v_price := &p_price;
v_inv_value := v_price * v_quantity;
dbms_output.put_line('******');
dbms_output.put_line('price * quantity=');
dbms_output.put_line(v_inv_value);
END;
/
Note: PL/SQL not designed for user interface programming
SELECT INTO
SET SERVEROUTPUT ON
DECLARE
v_max_gpa student.gpa%type;
v_numstudents number(4);
v_lname students.lname%type;
v_major students.major%type;
BEGIN
select max(gpa) into v_max_gpa
from students;
DBMS_OUTPUT.PUT_LINE ('The highest GPA is '||v_max_gpa);
select count(sid) into v_numstudents
from students
where gpa = v_max_gpa;
IF v_numstudents > 1 then
DBMS_OUTPUT.PUT_LINE ('There are '||v_numstudents||' with that GPA');
ELSE
select lname, major into v_lname, v_major
from students
where gpa=v_max_gpa;
DBMS_OUTPUT.PUT_LINE ('The student name is '||v_lname);
DBMS_OUTPUT.PUT_LINE ('The student major is '||v_major);
END IF;
END;
/
%ROWTYPE example with
implicit cursor
Set serveroutput on
DECLARE
v_student students%rowtype;
BEGIN
select * into v_student
from students
where sid='123456';
DBMS_OUTPUT.PUT_LINE (v_student.lname);
DBMS_OUTPUT.PUT_LINE (v_student.major);
DBMS_OUTPUT.PUT_LINE (v_student.gpa);
END;
/
Conditional Structures
IF-THEN
IF-THEN-ELSE
IF-THEN-ELSIF
An alternative to nested IF-THEN_ELSE
Syntax for Conditional Structures
COMMON PL/SQL STRING FUNCTIONS
CHR(asciivalue)
ASCII(string)
LOWER(string)
SUBSTR(string,start,substrlength)
LTRIM(string)
RTRIM(string)
LPAD(string_to_be_padded, spaces_to_pad, |string_to_pad_with|)
RPAD(string_to_be_padded, spaces_to_pad, |string_to_pad_with|)
REPLACE(string, searchstring, replacestring)
UPPER(string)
INITCAP(string)
LENGTH(string)
COMMON PL/SQL NUMERIC FUNCTIONS
ABS(value)
ROUND(value, precision)
MOD(value,divisor)
TRUNC(value,|precision|)
LEAST(exp1, exp2)
GREATEST(exp1, exp2)
IF-THEN-ELSIF Statements
. . .
IF rating > 7 THEN
v_message := 'You are great';
ELSIF rating >= 5 THEN
v_message := 'Not bad';
ELSE
v_message := 'Pretty bad';
END IF;
. . .
Iterative Structures
LOOP EXIT END LOOP
EXIT with an If Avoids Infinite Loop
LOOP EXIT WHEN END LOOP
Do Not Need An If to Control EXIT
WHILE LOOP END LOOP
Eliminates Need for EXIT
FOR IN END LOOP
Eliminates Need for Initialization of Counter
Simple Loop Example
DECLARE
i number_table.num%TYPE := 1;
BEGIN
LOOP
INSERT INTO number_table
VALUES(i);
i := i + 1;
EXIT WHEN i > 10;
END LOOP;
END;
CURSORS
A cursor contains a set of records resulted
from a SQL Statement
An Oracle Cursor = VB recordset = JDBC
ResultSet
Two Types of Cursors in PL/SQL
Implicit Automatically Created When a Query
or Manipulation is for a Single Row
Explicit Must Be Declared by the User
Creates a Unit of Storage Called a Result Set
Cursors Declaration
Declaring an Explicit Cursor
CURSOR CursorName IS SelectStatement;
Opening an Explicit Cursor
OPEN CursorName;
Accessing Rows from an Explicit Cursor
FETCH CursorName INTO RowVariables;
Controlling Cursor
No
Yes
DECLARE
DECLARE
Create a
named
SQL area
OPEN
OPEN
FETCH
FETCH
Identify the Load the
active set
current row
into
variables
EMPTY?
Test for
existing
rows
CLOSE
CLOSE
Release the
active set
Return to
FETCH if
rows found
31
Cursor Attributes
Obtain status information about a cursor.
Attribute
Type
Description
%ISOPEN
Boolean
Evaluates to TRUE if the cursor
is open
%NOTFOUND
Boolean
Evaluates to TRUE if the most
recent fetch does not return a row
%FOUND
Boolean
Evaluates to TRUE if the most
recent fetch returns a row;
complement of %NOTFOUND
%ROWCOUNT
Number
Evaluates to the total number of
rows returned so far
32
25463
Create or replace procedure proc_test as
v_empid employee.empid%type;
Cursor cur_sample is
Select empid from employee
Declare
where grade > 4;
Cursor
Data
returned
by
cursor
12245
55983
12524
98543
Open cursor for use.
Begin
open cur_sample;
loop
fetch cur_sample into v_empid;
exit when cur_sample%notfound;
update employee
set salary = salary + 500
where empid = v_empid;
end loop;
Stop
when not
more
records
areCommit;
found
Close cur_sample;
End;
Loops round each value
returned by the cursor
Place the value from the
cursor into the variable
v_empid
33
Using %TYPE and
%ROWTYPE
Declaring Variables of the Proper Type with
%TYPE
VarName TableName.FieldName%TYPE;
Declaring Variables to Hold An Entire Row
VarName CursorName%ROWTYPE;
Releasing the Storage Area Used by an
Explicit Cursor
CLOSE CursorName;
%Type Example
DECLARE
CURSOR students_cursor IS
SELECT last, major from students;
v_Last students.last%type;
v_major students.major%type;
BEGIN
DBMS_OUTPUT.PUT_LINE ('******************');
OPEN students_cursor;
FETCH students_cursor into v_last, v_major;
WHILE students_cursor%found LOOP
DBMS_OUTPUT.PUT_LINE (v_last);
DBMS_OUTPUT.PUT_LINE (v_major);
DBMS_OUTPUT.PUT_LINE ('******************');
FETCH students_cursor into v_last, v_major;
END LOOP;
CLOSE students_cursor;
END;
/
% ROWTYPE Example with
Cursor
DECLARE
cursor c is select * from number_table;
cVal c%ROWTYPE;
BEGIN
open c;
LOOP
fetch c into cVal;
EXIT WHEN c%NOTFOUND;
insert into doubles values(cVal.num*2);
END LOOP;
END;
FOR Loop and Cursor For Loop
DECLARE
i
number_table.num%TYPE;
BEGIN
FOR i IN 1..10 LOOP
INSERT INTO number_table VALUES(i);
END LOOP;
END;
DECLARE
cursor c is select * from number_table;
BEGIN
for num_row in c loop
insert into doubles_table
values(num_row.num*2);
end loop;
END;
/
Sample Cursor Program
(same program with %ROWTYPE)
DECLARE
CURSOR students_cursor IS
SELECT * from students;
v_student students_cursor%rowtype;
/* instead we could do v_student students%rowtype */
BEGIN
DBMS_OUTPUT.PUT_LINE ('******************');
OPEN students_cursor;
FETCH students_cursor into v_student;
WHILE students_cursor%found LOOP
DBMS_OUTPUT.PUT_LINE (v_student.last);
DBMS_OUTPUT.PUT_LINE (v_student.major);
DBMS_OUTPUT.PUT_LINE ('******************');
FETCH students_cursor into v_student;
END LOOP;
CLOSE students_cursor;
END;
/
Creating a PL/SQL Record
DECLARE
TYPE sailor_record_type IS RECORD
(sname
VARCHAR2(10),
sid
VARCHAR2(9),
age NUMBER(3),
rating
NUMBER(3));
sailor_record sailor_record_type;
...
BEGIN
Sailor_record.sname:=peter;
Sailor_record.age:=45;
Why Bulk Collect
Oracle uses two engines to process PL/SQL code. All procedural code is
handled by the PL/SQL engine while all SQL is handled by the SQL
statement executor, or SQL engine
There is an overhead associated with each context switch between the two
engines. If PL/SQL code loops through a collection performing the same
DML operation for each item in the collection it is possible to reduce
context switches by bulk binding the whole collection to the DML
statement in one operation.
Bulk Collect with Example
SET SERVEROUTPUT ON
DECLARE
TYPE t_bulk_collect_test_tab IS TABLE OF bulk_collect_test%ROWTYPE;
l_tab t_bulk_collect_test_tab;
CURSOR c_data IS
SELECT *
FROM bulk_collect_test;
BEGIN
OPEN c_data;
LOOP
FETCH c_data
BULK COLLECT INTO l_tab LIMIT 10000;
EXIT WHEN l_tab.count = 0;
-- Process contents of collection here.
DBMS_OUTPUT.put_line(l_tab.count || ' rows');
END LOOP;
CLOSE c_data;
END;
FORALL with example
DECLARE
TYPE t_tab IS TABLE OF exception_test%ROWTYPE;
ex_dml_errors EXCEPTION;
PRAGMA EXCEPTION_INIT(ex_dml_errors, -24381);
BEGIN
-- Fill the collection.
FOR i IN 1 .. 100 LOOP
l_tab.extend;
l_tab(l_tab.last).id := i;
END LOOP;
BEGIN
FORALL i IN l_tab.first .. l_tab.last SAVE EXCEPTIONS
INSERT INTO exception_test
VALUES l_tab(i);
EXCEPTION
WHEN ex_dml_errors THEN
l_error_count := SQL%BULK_EXCEPTIONS.count;
DBMS_OUTPUT.put_line('Number of failures: ' || l_error_count);
FOR i IN 1 .. l_error_count LOOP
DBMS_OUTPUT.put_line('Error: ' || i ||
' Array Index: ' || SQL%BULK_EXCEPTIONS(i).error_index ||
' Message: ' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE));
END LOOP;
END;
END;
/
l_tab
t_tab := t_tab();
l_error_count NUMBER;
Stored Procedures
PL/SQL code stored in the database and executed when called by the
user. Called by procedure name from another PL/SQL block or using
EXECUTE
CREATE [OR REPLACE] PROCEDURE procedure_name
[(parameter1 [mode1] datatype1,
parameter2 [mode2] datatype2,
. . .)]
IS|AS
PL/SQL Block;
Modes:
IN: procedure must be called with a value for the parameter. Value
cannot be changed
OUT: procedure must be called with a variable for the parameter.
Changes to the parameter are seen by the user (i.e., call by reference)
IN OUT: value can be sent, and changes to the parameter are seen by
the user
Default Mode is: IN
Stored Procedures
For example, below SP can be executed from sqlplus in 2 ways
SQL> EXEC SQR(50);
SQL> BEGIN SQR(50); END;
/
Create procedure SQR (v_num_to_square IN number)
AS
v_answer number(10);
BEGIN
v_answer := v_num_to_square * v_num_to_square;
dbms_output.put_line(v_answer);
END;
/
Function
PL/SQL user defined function stored in the database and executed when a
function call is made in code: example x := SQUARED(50)
Example:
Create or Replace Function SQUARED
(p_number_to_square IN number)
RETURN number
IS
v_answer number(10);
BEGIN
v_answer := p_number_to_square * p_number_to_square;
RETURN(v_answer);
END;
/
Creating a Function
Almost exactly like creating a procedure, but you
supply a return type
CREATE [OR REPLACE] FUNCTION
function_name
,parameter1 [mode1] datatype1[)
,parameter2 [mode2] datatype2
[). . .
RETURN datatype
IS|AS
;PL/SQL Block
Function Example
create
create or
or replace
replace function
function
rating_message(rating
rating_message(rating IN
IN NUMBER)
NUMBER)
return
return VARCHAR2
VARCHAR2
NOTE THAT YOU DON'T
AS
AS
SPECIFY THE SIZE
BEGIN
BEGIN
IF
IF rating
rating >> 77 THEN
THEN
return
return 'You
'You are
are great';
great';
ELSIF
ELSIF rating
rating >=
>= 55 THEN
THEN
return
return 'Not
'Not bad';
bad';
ELSE
ELSE
return
return 'Pretty
'Pretty bad';
bad';
END
END IF;
IF;
END;
END;
//
:Creating a function
create or replace function squareFunc(num in number)
return number
is
BEGIN
return num*num;
End;
/
:Using the function
BEGIN
dbms_output.put_line(squareFunc(3.5));
END;
/
Triggers
PL/SQL code executed automatically in response to a database event, typically
DML.
Like other stored procedures, triggers are stored in the database.
Often used to:
enforce complex constraints, especially multi-table constraints. Financial posting
is an example of this.
Trigger related actions
implement auditing logs
pop a sequence when creating token keys
Triggers do not issue transaction control statements (such as commit).
Triggers are part of the SQL transaction that invoked them.
USER_TRIGGERS provides a data dictionary view of triggers.
Triggers vs. Procedures
Procedures are explicitly executed by a user
or application
Triggers are implicitly executed (fired)
when the triggering event occurs
Triggers should not be used as a lazy way to
invoke a procedure as they are fired every
time the event occurs
Triggers Timing
A triggers timing has to be specified first
Before (most common)
Trigger should be fired before the operation
i.e. before an insert
After
Trigger should be fired after the operation
i.e. after a delete is performed
Trigger Events
Three types of events are available
DML events
DDL events
Database events
DML Events
Changes to data in a table
Insert
Update
Delete
DDL Events
Changes to the definition of objects
Tables
Indexes
Procedures
Functions
Others
Include CREATE, ALTER and DROP statements on
these objects
Database Events
Server Errors
Users Log On or Off
Database Started or Stopped
Trigger Level
Two levels for Triggers
Row-level trigger
Requires FOR EACH ROW clause
If operation affects multiple rows, trigger fires once for
each row affected
Statement-level trigger
DML triggers should be row-level
DDL and Database triggers should not be rowlevel
DML Triggers Syntax
CREATE OR REPLACE TRIGGER <trigger_name>
[BEFORE/AFTER][DELETE/INSERT/UPDATE of <column_name |, column_name |>
ON <table_name>
|FOR EACH ROW|
|WHEN <triggering condition>|
|DECLARE|
BEGIN
trigger statements
END;
To delete a trigger use:
DROP TRIGGER <trigger_name>;
Log Trigger Example
CREATE OR REPLACE TRIGGER LOGSTUDENTCHANGES
BEFORE INSERT OR DELETE OR UPDATE of Major ON STUDENTS
FOR EACH ROW
DECLARE
v_ChangeType CHAR(1);
v_sid varchar2(10);
BEGIN
IF INSERTING THEN
V_ChangeType := 'I';
v_sid := :new.sid;
ELSIF UPDATING THEN
V_ChangeType := 'U';
v_sid := :new.sid;
ELSE
V_ChangeType := 'D';
v_sid := :old.sid;
END IF;
INSERT INTO MAJ_AUDIT (change_type, changed_by, timestamp,
SID, old_major, new_major)
VALUES (v_ChangeType, USER, SYSDATE, v_sid, :old.major, :new.major);
END LOGSTUDENTCHANGES;
UpperCase Trigger Example
CREATE OR REPLACE TRIGGER UPPERCASE
BEFORE INSERT OR UPDATE ON STUDENTS
FOR EACH ROW
DECLARE
BEGIN
:new.lastname:=UPPER(:new.lastname);
:new.firstname:=UPPER(:new.firstname);
END UPPERCASE;
/
Mutating Table Errors
Occurs when a row-level trigger attempts to read or write the table from which the trigger
was fired
Example
TRIGGER brake_on_raises
BEFORE UPDATE OF salary ON employee
FOR EACH ROW
DECLARE
l_curr_max NUMBER;
BEGIN
SELECT MAX (salary) INTO l_curr_max
FROM employee;
IF l_curr_max * 1.20 < :NEW.salary
THEN
errpkg.RAISE (
employee_rules.en_salary_increase_too_large,
:NEW.employee_id,
:NEW.salary
);
END IF;
END;
Guidelines to keep in mind regarding
mutating table errors
In general, a row-level trigger may not read or write the
table from which it has been fired. The restriction applies
only to row-level triggers, however. Statement level triggers
are free to both read and modify the triggering table; this
fact gives us a way to avoid the mutating table error.
If you make your trigger an autonomous transaction (by
adding the PRAGMA AUTONOMOUS TRANSACTION
statement and committing inside the body of the trigger),
then you will be able to query the contents of the firing
table. However, you will still not be allowed to modify the
contents of the table.
Creating a DDL Trigger
To create (or replace) a DDL trigger, use the syntax shown
here:
1 CREATE [OR REPLACE] TRIGGER trigger name
2 {BEFORE | AFTER } { DDL event} ON {DATABASE |
SCHEMA}
3 [WHEN (...)]
4 DECLARE
DDL Triggers | 673
5 Variable declarations
6 BEGIN
7 ...some code...
8 END;
Exception Handling
Remember
it is optional
[DECLARE]
BEGIN
[EXCEPTION]
END;
63
More on Exception Handling
Exceptions
are pre-defined or designerspecified occurrences during the operation of a
PL/SQL block
Exceptions are named by user or by Oracle
Exceptions are raised internally by PL/SQL or
explicitly by designer using RAISE keyword
A routine in the Exception section will then be
called
64
Common Pre-defined Exceptions
Oracle Error
Exception
description
ORA-00001
DUP_VAL_ON_INDEX
PK violation
ORA-01403
NO_DATA_FOUND
No records
ORA-01422
TOO_MANY_ROWS
> 1 row
ORA-01476
ZERO_DIVIDE
ORA-01722
INVALID_NUMBER
65
Note: there are others too!
Cant convert
Example using pre-defined method
--This example returns salaries for branch 10
DECLARE
v_salary personnel.salary%type;
BEGIN
select salary into v_salary from personnel where div=10;
EXCEPTION
when too_many_rows -- this is the pre-defined exception
then raise_application_error (-20001, ' Did not expect so many');
END;
/
declare
*
ERROR at line 1:
ORA-20001: Did not expect so many
ORA-06512: at line 7
66
If you dont trap it Oracle takes over!
--in this example branch 40 has no staff yet!
DECLARE
v_salary personnel.salary%type;
BEGIN
select salary into v_salary from personnel where div=40;
EXCEPTION
when too_many_rows -- this is the pre-defined exception
then raise_application_error (-20001, ' Did not expect so many');
END;
/
This is NO_DATA_FOUND exception
declare
*
ERROR at line 1:
ORA-01403: no data found occurs as div 40 has no staff!
67ORA-06512: at line 4
Exception Handling Example
DECLARE
num_row number_table%ROWTYPE;
BEGIN
select *
into num_row
from number_table;
dbms_output.put_line(1/num_row.num);
EXCEPTION
WHEN NO_DATA_FOUND THEN
dbms_output.put_line('No data!');
WHEN TOO_MANY_ROWS THEN
dbms_output.put_line('Too many!');
WHEN OTHERS THEN
dbms_output.put_line(Error);
end;
User-defined
Declare
a name for the exception
Identify point to raise exception
Defining code to fire when raised
DECLARE
69
Salary_too_high
EXCEPTION;
Invalid_tax_code
EXCEPTION;
RAISING
Once
control has passed to the Exception
section it cannot be returned to the block that
raised it
RAISE exception_name
IF v_salary>v_max then
RAISE salary_too_high;
End if
70
Jump to the
exception section
user defined as
salary_too_high
RAISE_APPLICATION _ERROR
We
have seen this already
Error_number is a negative integer in the range
-20000 to -20999
Error_message is a character string up to 512
bytes
Raise_application_error)error_number,error_message)
71
EXCEPTION Section
EXCEPTION
WHEN salary_too_high
then PL/SQL statements here;
WHEN another_error OR yet_another
then PL/SQL statements here;
WHEN OTHERS
-- Oracle defined
then PL/SQL statements here;
END;
72
Note OTHERS will trap any other error that you have
not accounted for
Example and error generated
DECLARE
v_bonus number;
BEGIN
select bonus into v_bonus from personnel where snum=3813;
if v_bonus is null then
raise_application_error(-20111,'For goodness sake give him a
bonus!!');
end if;
END;
/
declare
*
ERROR at line 1:
ORA-20111: For goodness sake give him a bonus!!
ORA-06512: at line 6
73
Example
set serveroutput on
DECLARE
v_bonus number;
null_bonus_alert exception;
BEGIN
select bonus into v_bonus from personnel where snum=3813;
if v_bonus is null then
raise null_bonus_alert;
end if;
EXCEPTION
when null_bonus_alert then
dbms_output.put_line('This exmployee really should get a bonus!');
END;
/
74
User-Defined Exception
DECLARE
e_number1 EXCEPTION;
cnt
NUMBER;
BEGIN
select count(*)
into cnt
from number_table;
IF cnt = 1 THEN RAISE e_number1;
ELSE dbms_output.put_line(cnt);
END IF;
EXCEPTION
WHEN e_number1 THEN
dbms_output.put_line('Count = 1');
end;
Packages
A collection
of PL/SQL objects that are logically
grouped together to form one unit
They can contain:
Procedures, functions
Cursors, variables, Constants
Tables
Exceptions
Typically,
they may contain all routines to
process purchase orders, for example.
76
Package structure
Has
2 parts:
Package Specification
Declares
public procedures etc
Other programs can access them outside the package
Package Body
Implements
77
the public procedures etc but also may specify
private procedures, functions etc
The private units are only accessible within the scope of
the package itself
All units declared in specification MUST be implemented in
the body
Package Specification example
CREATE OR REPLACE PACKAGE package_name
IS
PROCEDURE sal_raise
(p1 NUMBER, p2 NUMBER);
Note there is
--------------------------------------------no PL/SQL
FUNCTION div_bal
executable
(div_no IN NUMBER)
code
RETURN NUMBER;
--------------------------------------------END package_name; -- not necessary to name package here
-- just for clarity
78
Package Body
CREATE OR REPLACE PACKAGE BODY package_name
IS
PROCEDURE sal_raise (p1 NUMBER, p2 NUMBER)
IS
BEGIN
update staff set salary=salary*1.1 where div=p2;
END sal_raise;
--------------------------------------------FUNCTION div_bal (div_no IN NUMBER)
RETURN NUMBER
IS
bal number;
BEGIN
select sum(salary) into bal from staff where div=div_no;
RETURN bal;
END div_bal;
79END package_name;
How do we use them?
DROP PACKAGE package_name
DROP PACKAGE BODY package_name
Will remove specification and body
Will only remove the body
To run/access an element of a package body
Execute package_name.element
empName:=package_name.getName)empID);
80
The package
The function element The parameter
Global variables
CREATE OR REPLACE PACKAGE BODY stdcomp
IS
gcompany NUMBER; -- global to the package
PROCEDURE setcomp (xcompany IN NUMBER) IS
BEGIN
gcompany:=xcompany;
END setcomp;
--------------------------------------------FUNCTION getcomp
RETURN NUMBER IS
BEGIN
RETURN NVL(gcompany,0);
END getcomp;
--------------------------------------------END stdcomp;
81
Instantiation and persistence
Instantiation
occurs each time you connect to
the database
So any state of your current session is lost
when this happens
And packages are instantiated again
The default behaviour of a package is to
maintain its state once it has been instantiated
throughout the life of the session
82
Persistence
CREATE OR REPLACE PACKAGE pack1 IS
V1 NUMBER:=1;
Procedure proc1;
End pack1;
CREATE OR REPLACE PACKAGE BODY pack1 IS
V2 NUMBER:=2;
Procedure proc1 IS
V3 NUMBER:=3;
BEGIN
v1:=v1+1;
v2:=v2+2;
v3:=v3+3;
DBMS_OUTPUT.PUT_LINE)v1 = ||v1);
DBMS_OUTPUT.PUT_LINE)v2 = ||v2);
DBMS_OUTPUT.PUT_LINE)v3 = ||v3);
END proc1;
END pack1;
83
Execute pack1.proc1 do it 3 times!
execution
1st
2nd
3rd
v1 2 3 4
v2 4 6 8
v3 6 6 6
Pragma SERIALLY_REUSABLE
Causes the PL/SQL runtime to discard the state of
the package. So instantiation occurs each time it is
invoked
Pragma serially_reusable needs to be applied to
both the SPECIFICATION and the BODY
Now, execution 3 times of the previous code would
be as follows
v1 2 2 2
v2 4 4 4
84
v3 6 6 6
CREATE OR REPLACE PACKAGE pack1
IS
Pragma serially_reusable;
V1
NUMBER:=1;
Procedure proc1;
End pack1;
Overloading
Sub-programs
in a package body can have the
same names so long as parameter lists are not
the same.
E.g. the TO_CHAR function in SQL takes either
a number or a date.
The appropriate function is called depending
on what the user enters.
85
Overload example
CREATE OR REPLACE PACKAGE overload IS
Function sal_return )p_detail NUMBER)
Return NUMBER;
Function sal_return )p_detail VARCHAR2)
Return NUMBER;
End overload;
CREATE OR REPLACE PACKAGE BODY overload IS
Function sal_return )p_detail NUMBER)
Return NUMBER IS
v_salary NUMBER;
BEGIN
Select salary into v_salary from staff where snum=p_detail;
Return v_salary;
END sal_return;
86
Code Continues on next slide
Continued ..
Same name different
parameter datatype
Different
attribute
Function sal_return )p_detail VARCHAR2)
Return NUMBER IS
v_salary NUMBER;
BEGIN
Select salary into v_salary from staff where surname=p_detail;
Return v_salary;
END sal_return;
END overload;
SELECT overload.sal_return)3488) from dual ; -- this would call the first
sal_return
SELECT overload.sal_return)STYLES) from dual; -- this would call the second
sal_return
87
Legal and illegal packages!
CREATE OR REPLACE PACKAGE ovtest
LEGAL as positions
IS
Function cat )n1 NUMBER, c2 VARCHAR2) are different
Return VARCHAR2;
---------------------------------------------Function cat )c1 VARCHAR2, n2 NUMBER)
Return VARCHAR2;
CREATE OR REPLACE PACKAGE ovtest
End ovtest;
IS
Function cat )n1 NUMBER, c2 VARCHAR2)
Return VARCHAR2;
---------------------------------------------------------Function cat )n1 INTEGER, c2 CHAR)
ILLEGAL as parameters
Return VARCHAR2;
in both are compatible
End ovtest;
88
Dynamic SQL
Dynamic SQL refers to SQL statements that are
constructed and executed at runtime.
Syntax:
EXECUTE IMMEDIATE SQL_string
[INTO {define_variable[, define_variable]... |
record}]
[USING [IN | OUT | IN OUT] bind_argument
[, [IN | OUT | IN OUT] bind_argument]...];
89
SQL_string
Is a string expression containing the SQL statement or PL/SQL block.
define_variable
Is a variable that receives a column value returned by a query.
record
Is a record based on a user-defined TYPE or %ROWTYPE that receives an
entire row returned by a query.
bind_argument
Is an expression whose value is passed to the SQL statement or PL/SQL
block, or an identifier that serves as an input and/or output variable to the function or
procedure that is called in the PL/SQL block.
INTO clause
Is used for single-row queries; for each column value returned by the query,
you must supply an individual variable or field in a record of a compatible type.
USING clause
Allows you to supply bind arguments for the SQL string. This clause is used
for both dynamic SQL and PL/SQL, which is why you can specify a parameter mode.
This mode is relevant only for PL/SQL, however; the default is IN, which is the only
kind of bind argument you would have for SQL statements.
90
Dynamic SQL example
PROCEDURE wrong_incentive (
company_in IN INTEGER,
new_layoffs IN NUMBER
)
IS
sql_string VARCHAR2(32767);
sal_after_layoffs NUMBER;
BEGIN
sql_string :=
'UPDATE ceo_compensation
SET salary = salary + 10 * :layoffs
WHERE company_id = :company
RETURNING salary INTO :newsal';
EXECUTE IMMEDIATE sql_string
USING new_layoffs, company_in, OUT sal_after_layoffs;
DBMS_OUTPUT.PUT_LINE (
'CEO compensation after latest round of layoffs $' || sal_after_layoffs);
END;
91
Dynamic PLSQL Example/Utility
PROCEDURE dynPLSQL (blk IN
VARCHAR2)
IS
BEGIN
EXECUTE IMMEDIATE
'BEGIN ' || RTRIM (blk, ';') || '; END;';
END;
exec dynPLSQL ('calc_totals');
92