plsqltut

28
PL/SQL Workbook For use in association with: http://www.shu.ac.uk/schools/cms/teaching/pl3/ sqlplsqlreminder.doc © Sheffield Hallam University Version 2 School of Computing and Management Sciences

description

plsqltut

Transcript of plsqltut

This is the title

PL/SQL WorkbookPage 20PL/SQL WorkbookPage 19

PL/SQL Workbook

For use in association with: http://www.shu.ac.uk/schools/cms/teaching/pl3/sqlplsqlreminder.docWhat 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 machine-readable p-code at compile time (early binding) and then, at run time, the PL/SQL engine simply executes the p-code.

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 sub-programme 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

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

WHILE...LOOP/END LOOP

. assignment statements such as X:= Y + Z ;

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.

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 answerselect * from stats ;

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.

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!

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

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.

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_BalEXCEPTION ;

You can then choose to raise Negative_Bal in your executable code, remembering to handle it appropriately.

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 ;

/

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 --note the colon to denote global variable

PL/SQL procedure successfully completed.

SQL> print :test

TEST

------------------------------------------------------------

The date is: 02-Feb-04Of 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.

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;

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=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

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 ;

/

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 ;

/

Trigger to run CountEmpsCREATE OR REPLACE TRIGGER doempcount

AFTER DELETE OR INSERT ON emp

BEGIN

countemps ;

END;

/Audit trail triggerCREATE OR REPLACE TRIGGER deptaudit

AFTER INSERT ON dept

BEGIN

INSERT INTO DEPT_AUDIT values(SYSDATE,USER,'A new dept was created today');

END;

/

DECLARE v_below avgsal.Above%type ;

v_v_etc....

cursor ---------- IS

etc...

etc.......

Sheffield Hallam University

Version 2School of Computing and Management Sciences

Peter Lake Sheffield Hallam University

Version 1School of Computing and Management Sciences

Peter Lake Sheffield Hallam University

Version 1School of Computing and Management Sciences