PL/SQL Workbook - Sheffield Hallam University

advertisement
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 Oracle’s procedural extension to SQL*Plus. The advantage
that SQL’s 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 Oracle’s 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 doesn’t 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
Exceptions
Page 9
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
Functions and
Procedures
Page 10
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
Triggers
Page 12
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, don’t
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;
Example trigger
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
Sample Solutions
Average Salary
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 ;
IF v_dep<>v_lastdep THEN
-- if this isnt the first time through, write the answer out
Peter Lake
Version 1
© Sheffield Hallam University
School of Computing and Management Sciences
PL/SQL Workbook
Page 15
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
Top 10 Employees
Page 16
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
Alphabet Stats
Page 17
-- 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 ;
LOOP
Fetch Alphabet_Cursor INTO V_Alpha ;
IF V_Alpha=V_letter then
UPDATE stats
Peter Lake
Version 1
© Sheffield Hallam University
School of Computing and Management Sciences
PL/SQL Workbook
Page 18
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 ;
The Date Is
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
Counting Employees
Page 19
-- 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
Trigger to run
CountEmps
Page 20
CREATE OR REPLACE TRIGGER doempcount
AFTER DELETE OR INSERT ON emp
BEGIN
countemps ;
END;
/
Audit trail trigger
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
Download