You are on page 1of 20

PL/SQL Workbook

For use in association with:


http://www.shu.ac.uk/schools/cms/teaching/pl3/sqlplsqlreminder.doc

Version 2

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 2

What is PL/SQL?

What is PL/SQL used for?


PL/SQL is Oracles procedural extension to SQL*Plus. The advantage
that SQLs non-procedural approach has to offer - that you can state what
you want done without having to state how to do it comes at a price:
loss of control. PL/SQL gives programmers some control back.
As an SQL extension, PL/SQL supports the standard DML commands.
PL/SQL lets you use all the SQL data manipulation, cursor control, and
transaction control commands, as well as all the SQL functions,
operators, and pseudo-columns. You cannot, however, use DDL
commands.
PL/SQL is used by many of Oracles tools, including Forms and
Reports. You also need to master PL/SQL for writing stored procedures,
functions and packages for use with Triggers.
Where does PL/SQL live?
The PL/SQL engine may live either client side, perhaps with a tool like
SQLPLUS, or on the server. These two environments are independent.
PL/SQL might be available in the Oracle Server but unavailable in tools,
or the other way around. In either environment, the PL/SQL engine
accepts as input any valid PL/SQL block or subprogram
The engine executes procedural statements but sends SQL statements to
the SQL Statement Executor in the Oracle Server.
SQL is compiled and executed statement-by-statement at run time,
referred to as late binding. PL/SQL, however, is processed into machinereadable p-code at compile time (early binding) and then, at run time, the
PL/SQL engine simply executes the p-code.

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 3

PL/SQL Structure
PL/SQL code is written in blocks, which may be nested. Each block will
have the following structure:
Declarative: Keyword = DECLARE. This contains the definitions of
variables and other elements, such as cursors, constants, tables, etc.
Executable: The only required part. The executable statements are
placed between a BEGIN and END;
Exception Handling: Keyword = EXCEPTION allows neat exits
from problems.
Blocks may be named. They may form the basis of one of 2 subprogramme types: a procedure or a function. There are loops, conditions
and assignments, much as you would expect of a regular procedural 3GL
language.
PL/SQL is modular: several related procedures and functions may be
parcelled up into a Package.
The scope of a declared identifier is that region of a program unit (block,
subprogram, or package) from which you can reference the identifier.
Identifiers declared in a PL/SQL block are considered local to that block
and global to all its sub-blocks.
PL/SQL Blocks may contain :
.

SQL*PLUS DML and trasaction processing statements.


flow of control statements such as IF...THEN...ELSE, EXIT

Peter Lake
Version 1

repetition statements such as FOR...LOOP/END LOOP and


WHILE...LOOP/END LOOP

assignment statements such as X:= Y + Z ;

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 4

Cursors
Cursors can be define in the declaration. They act as a pointer to the
current row of a result table of an SQL query. They need to be OPENed
before use, rows can be FETCHed into variables, and, upon completion, a
cursor should be CLOSEd to free memory.
For example:
DECLARE
CURSOR depts_cursor IS
select deptno, count(*) from emp group by deptno ;
In this example we have decared a CURSOR, called depts_cursor. Once it
is opened the recordset that results from the SQL query is returned to the
PLSQL process for use.
Attributes
PL/SQL variables and cursors have attributes, which are properties that
let you reference the datatype and structure of an item without needing to
repeat its definition. Database columns and tables have similar attributes,
which you can also use.
Perhaps the most useful attribute is %TYPE which provides the
datatype of a variable or database column. This is particularly useful
when declaring variables that will hold database values, for example:
DECLARE
v_deptno

emp.deptno%TYPE ;

Here the variable called v_deptno is declared to be of the same type as


the column called deptnlo in the EMP table.

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 5

A Worked Example

Some of the topics discussed above can be found in the following code.
The purpose of this simple little programme is to create a statistics table
which stores statistics for each department.
-- PL/SQL doesnt support DDL so do the table creates first
DROP TABLE stats ;
CREATE TABLE stats (Deptno integer, StaffCount integer) ;
-- begin the PL/SQL code
DECLARE
v_count

BINARY_INTEGER ;

v_deptno

emp.deptno%TYPE ;

CURSOR depts_cursor IS
select deptno, count(*) from emp group by deptno ;
BEGIN
-- start by activating the cursor
open depts_cursor ;
LOOP
-- put the values from this row into our predefined variables
FETCH depts_cursor INTO v_deptno, v_count ;
--exit when system variable NOTFOUND is set to true
EXIT when depts_cursor%NOTFOUND ;
INSERT INTO stats VALUES(v_deptno, v_count) ;
END LOOP ;
CLOSE depts_cursor ;
END ;
-- tell Oracle to compile and execute
/
-- check that we have the right answer
select * from stats ;

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 6

Exercises

Some exercises follow to get you into the swing of PL/SQL-ing. The first
one has some tips on the way, but the last few are up to you!

Guided Example
You are asked to create a table which stores the number of employees
(NOT including the boss) who earn above the average salary for the
company, and the number who earn less or the same as the average, for
each department.
The modularity of PL/SQL lends itself to breaking larger problems down
into several smaller ones. Of course, you may choose to approach this
problem in whatever way you like, but here is one suggestion:
1. Write the SQL code to create the table to store the answers

2. Decide what variable(s) you will need during in the execution and place them
in a DECLARE. Oracle like us to stick to a naming convention of v_ for
variables.

DECLARE
v_below

avgsal.Above%type ;

v_
v_
etc....

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 7

3. Decide what DML we need to work on (the cursors). We need 2 cursors in


this example one to calculate the average, and one to go through EMP
checking each salary against the average. TIP: Try the sql first in
SQLPLUSW to make sure it brings back what you need!

cursor ---------- IS
etc...
etc.......
4. Write the BEGIN of the executable section. One tip is to write the END at the
same time and add the code in between the two.
5. We need to establish what the average salary is (by opening a cursor and
fetching a value INTO v_avg)
6. We need to loop through the EMP table. Write the iterative control code
(LOOP....END LOOP). Don't forget your exit strategy, otherwise you will
keep looping forever!
7. Dont forget you need to open and close cursors at appropriate times.
8. Apply your test to each row, and increment your variable accordingly. Then
write the INSERT code to put the data into your newly created table. The
format for IF blocks is: if x then y; else z; end if;

An answer to the above, and all the exercises, can be


found at the end of these notes

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 8

More Exercises
1. A useful attribute of a cursor is %ROWCOUNT. It records the number
of rows fetched so far. (usage: cursorname%ROWCOUNT). You
should use it to pick out the ten highest paid employees and store
their name, job and salary in a table.
2. You a required to build a table which has 24 rows, a field called letter,
and a field called count. Use PL/SQL to create this table with each
letter of the alphabet. (HINT: CHR() turns an ordinal into a CHAR,
and 65=A.) The count should have the count of employee names
beginning with that letter (HINT: Substr()), including 0 when no
names commence with the letter in question.

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 9

Exceptions

Up until now we have only used the first two parts of a PL/SQL block.
The exception part can be very useful in helping us control error
situations cleanly.
In PL/SQL a warning or error condition is called an exception. Some
common internal exceptions have predefined names, such as
ZERO_DIVIDE and NO_DATA_FOUND.
When an error occurs, an exception is raised and normal execution stops
with control transferring to the exception-handling part of the PL/SQL
block or subprogram. Here is a sample of some exception handling note
the use of the catch-all OTHERS exception:
BEGIN
..........................some lines of pl/sql code..................................
EXCEPTION
WHEN DUP_VAL_ON_INDEX THEN
INSERT INTO errtable VALUES
(v_ename,v_position,v_sal);
COMMIT;
WHEN ZERO_DIVIDE THEN
/* deal with these */
WHEN OTHERS THEN
/* deal with these as well */
END;

User Defined Exceptions.


It is possible to create you own exceptions, and handle them how you see
fit. You should declare your except thus:
Negative_Bal

EXCEPTION ;

You can then choose to raise Negative_Bal in your executable code,


remembering to handle it appropriately.

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 10

Functions and
Procedures

Functions and procedures are structured in the same way, except that
functions have a RETURN clause. The syntax for a function is:
FUNCTION name [(parameter[, parameter, ...])] RETURN datatype
IS
[local declarations]
BEGIN
executable statements
[EXCEPTION
exception handlers]
END [name];
Generally, tools like Oracle Forms which incorporate the PL/SQL engine
can store subprograms locally for later, strictly local execution. To make
your code available for general use by all tools, subprograms must be
stored in an Oracle database.
To create subprograms and store them permanently in an Oracle database,
you use the CREATE PROCEDURE and CREATE FUNCTION
statements, which you can execute from SQL*Plus. As an alternative, if
you are happy that you are not overwriting something valuable, there is
also the CREATE OR REPLACE statement.
As an example, this code will store a function for working out VAT.
CREATE OR REPLACE FUNCTION vat (v_netof NUMBER)
RETURN NUMBER IS
BEGIN
RETURN (v_netof*0.175) ;
END vat ;
/

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 11

Exercise
Create a stored function which returns the current date concatenated with
the phrase: The date is:
Once you get the reassuring message PL/SQL procedure successfully
completed. you can test your function out from the sql> prompt thus:
SQL> var test varchar2(50)
SQL> execute :test:=thedateis
variable

--note the colon to denote global

PL/SQL procedure successfully completed.


SQL> print :test
TEST
-----------------------------------------------------------The date is: 02-Feb-04
Of course you should make your functions and procedures more robust
with the use of EXCEPTIONS.

Exercise
Now create a procedure which puts the current count of employees into a
single row single column table called EMPCOUNTER.
Use the execute command to test your procedure.

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 12

Example trigger
Triggers

Triggers are used to guarantee that when a specific operation is


performed, related actions are carried out. A note of caution, however
many triggers make for a slow database!
A database trigger can be created thus:
CREATE TRIGGER triggername
[BEFORE or AFTER] [EVENT] ON tablename
BEGIN
--do something
END;
The FOR EACH ROW option determines whether the trigger is a row
trigger or a statement trigger and would follow the table name.
One fatal trap to avoid at all costs a recursive trigger. Furthermore, dont
define triggers that merely duplicate the functionality already built into
Oracle, such as referential integrity.
Triggers can be turned off and on, simply by using :
ALTER TRIGGER triggername DISABLE;
One shortcut is ALL TRIGGERS as with:
ALTER TABLE tablename ENABLE ALL TRIGGERS;
CREATE or REPLACE TRIGGER aud
AFTER INSERT ON DEPT
FOR EACH ROW
BEGIN
-- :new.deptno is the value that is entered in each row for the deptno
column
IF :new.deptno<50 then
INSERT INTO DEPT_AUDIT values(SYSDATE,USER,'This was
a sub 50 value');
END IF;
END;

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 13

Exercises

1 Create a trigger which automatically updates the empcounter table


whenever a new record is inserted, or a record is deleted.
2 Each time a new department is created, a audit table should have a
record inserted with date, user and some text to say what has
happened.

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 14

Average Salary

Sample Solutions

drop table avgsal ;


create table avgsal(deptno integer , Above Integer, Below Integer) ;
DECLARE
v_below

avgsal.Below%TYPE;

v_above

avgsal.Above%TYPE;

v_avg

emp.sal%TYPE ;

v_lastdep

integer ;

v_dep

integer ;

v_sal

emp.sal%TYPE ;

CURSOR c_avg IS select avg(sal) from emp ;


CURSOR c_emp IS select deptno, sal from emp where DeptNo IS
NOT NULL ORDER BY deptno ;
BEGIN
-- first discover what the average is, and pop it in a variable
OPEN c_avg ;
FETCH c_avg INTO v_avg ;
CLOSE c_avg ;
-- we need to write a row out when the department changes
v_lastdep:= 0 ;
OPEN c_emp ;
LOOP
-- go through the emp cursor line by line
FETCH c_emp INTO v_dep, v_sal;
EXIT WHEN c_emp%NOTFOUND ;

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 15

IF v_dep<>v_lastdep THEN
-- if this isnt the first time through, write the answer out

IF v_lastdep<>0 then
INSERT INTO avgsal VALUES (v_lastdep,
v_above,v_below) ;
END IF;
v_above:=0 ;
v_below:=0 ;
v_lastdep:=v_dep ;
END IF ;
IF v_sal <= v_avg THEN
v_below:=v_below+1 ;
ELSE
v_above:=v_above+1 ;
END IF ;

END LOOP;

-- catch the last department


INSERT INTO avgsal VALUES (v_lastdep,
v_above,v_below) ;
COMMIT;
CLOSE c_emp;
END;
/
select * from avgsal ;

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 16

Top 10 Employees

drop table top_emps ;


create table top_emps(name varchar2(10), job Varchar2(12), Salary
Number(7,2)) ;
DECLARE
v_ename

emp.ename%TYPE;

v_position

emp.job%TYPE;

v_sal

emp.sal%TYPE ;

CURSOR cur_top_emps IS
SELECT ename, job, sal
FROM emp
ORDER BY sal DESC ;
BEGIN
OPEN cur_top_emps ;
LOOP
FETCH cur_top_emps INTO v_ename, v_position,
v_sal;
EXIT WHEN cur_top_emps%ROWCOUNT > 10 ;
INSERT INTO top_emps
VALUES (v_ename, v_position,v_sal) ;
END LOOP;
COMMIT;
CLOSE cur_top_emps;
END;
/
select * from top_emps ;

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 17

Alphabet Stats

-- PL/SQL doesnt support DDL so do the table creates first


DROP TABLE stats ;
CREATE TABLE stats(letter varchar2(1),
Count integer) ;
DECLARE
v_count
v_letter

BINARY_INTEGER ;
Varchar2(1) ;

v_alpha
v_ord

Varchar2(1) ;
BINARY_INTEGER ;

CURSOR letters_cursor IS
select substr(ename,1,1), count(*) from emp
Group by substr(ename,1,1) ;
CURSOR alphabet_cursor IS select letter from stats order by
letter ;
BEGIN
-- create the alphabet
v_ord:=64 ;
LOOP
v_ord:=v_ord+1 ;
INSERT INTO stats values(CHR(v_ord),0) ;
exit when v_ord>=90 ;
END LOOP ;

open alphabet_cursor ;
open letters_cursor ;
LOOP
FETCH letters_cursor INTO v_letter, v_count ;
EXIT when letters_cursor%NOTFOUND ;
Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 18

The Date Is

LOOP
Fetch Alphabet_Cursor INTO V_Alpha ;
IF V_Alpha=V_letter then
UPDATE stats
SET Count = v_count
WHERE letter = v_letter ;
END IF ;
exit when V_Alpha=V_letter ;
END LOOP ;

END LOOP ;
COMMIT ;
CLOSE letters_cursor ;
CLOSE alphabet_cursor ;
end ;
/
-- check that we have the right answer
select * from stats ;

CREATE OR REPLACE FUNCTION thedateis


RETURN CHAR
IS
BEGIN
RETURN ('The date is: '||TO_CHAR(SYSDATE)) ;
END thedateis ;
/

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 19

Counting Employees

-- the table only needs creating once


drop table empcounter ;
create table empcounter(No_Emps INTEGER) ;
CREATE OR REPLACE PROCEDURE countemps
IS
--note there is no need for the key word DECLARE
v_cownt INTEGER ;
CURSOR cownt_cursor IS
select count(*) from emp ;
BEGIN
OPEN cownt_cursor ;
FETCH cownt_cursor INTO v_cownt ;
CLOSE cownt_cursor ;
DELETE FROM empcounter ;
INSERT INTO empcounter values(v_cownt) ;
END countemps ;
/

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

PL/SQL Workbook

Page 20

Audit trail
Trigger
to run
trigger
CountEmps

CREATE OR REPLACE TRIGGER doempcount


AFTER DELETE OR INSERT ON emp
BEGIN
countemps ;
END;
/

CREATE OR REPLACE TRIGGER deptaudit


AFTER INSERT ON dept
BEGIN
INSERT INTO DEPT_AUDIT values(SYSDATE,USER,'A new
dept was created today');
END;
/

Peter Lake
Version 1

Sheffield Hallam University


School of Computing and Management Sciences

You might also like