Procedures, Functions, Triggers and Packages: Objectives
Procedures, Functions, Triggers and Packages: Objectives
Procedures, Functions, Triggers and Packages: Objectives
OBJECTIVES
Creating procedures
Passing parameters and getting values out.
Dropping procedures
Creating Functions
Return statement
Dropping Functions
Creating package specifiacations
Creating package bodies
Calling stored packages
Cursors in packages
Advantages of packages
What is a trigger
Parts of a trigger
Before and After triggers
Statement and Row triggers
Enabling and Disabling Triggers
Dropping Triggers
A procedure or function can be invoked from most Oracle tools like SQL*Plus, or other
programming languages like C++ and JAVA.
2. Improved performance.
Avoid reparsing for multiple users by exploiting shared SQL.
Avoid PL/SQL parsing at run time by parsing at compile time.
Reduce the number of calls to the database and decrease network traffic by
bundling commands.
3. Improved maintenance.
Modify routines online without interfering with other users.
Modify one routine to affect multiple applications.
Modify one routine to eliminate duplicate testing.
PROCEDURES
Procedures are simply named PL/SQL blocks. They are created and owned by a particular
schema. Like the DML functions, right to execute a procedure can be granted / revoked.
Parameter list - If a procedure contains more than one parameter, commas should be used
to separate them. You cannot specify a constraint on the data type.
For example, it is illegal to do the following:
Variable declarations - Constants, variables, other procedures and local functions are
declared in this section. Unlike the parameter list, you must specify the constraint of the
data type of a local variable.
Executable commands - The commands to be executed by the procedure are placed here.
Exception handlers - Any error handling statements that should be caught while executing
the procedure are placed here.
A procedure has two parts: the specification and the body. The procedure specification
begins with the keyword PROCEDURE and ends with the procedure name or a
parameter list. Parameter declarations are optional. Procedures that take no parameters
are written without parentheses.
The procedure body begins with the keyword IS and ends with the keyword END
followed by an optional procedure name. The procedure body has three parts: a
declarative part, an executable part, and an optional exception-handling part.
The declarative part contains some local declarations, which are placed between the
keywords IS and BEGIN. The keyword DECLARE, which introduces declarations in an
anonymous PL/SQL block, is not used.
The executable part can hold one or more statements, which are placed between the
keywords BEGIN and EXCEPTION (or END). At least one statement must appear in the
executable part of a procedure. The NULL statement meets this requirement.
The exception-handling part contains the exception handlers, which should be placed
between the keywords EXCEPTION and END.
Before executing a procedure, you need to compile and load it to a schema. In other
words, you have to make it “available” in your session. To do so, run the following
statement at the SQL prompt:
SQL>@<SQL filename>
or
SQL>start <SQL filename>
Notice that it is also the same command to run an unnamed block. Bear in mind that the
filename needs NOT be the same as the procedure name. An .sql file ONLY contains the
code for the procedure. If it compiles correctly, it is the procedure that is loaded to the
database, not the .sql file. Therefore, in order to run a procedure at a SQL prompt, you
need to execute the procedure, not the .sql file (We will discuss further about later).
If you are prompted with a warning message, type the following command to check the
details about the error:
SET SERVEROUTPUT ON
DECLARE
temp_emp_sal NUMBER(10,2);
BEGIN
SELECT emp_salary
INTO temp_emp_sal
FROM employee
WHERE emp_ssn = '999666666';
IF temp_emp_sal > 4000 THEN
DBMS_OUTPUT.PUT_LINE('Salary > 4000');
ELSE
DBMS_OUTPUT.PUT_LINE('Salary < 4000');
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Employee not found ');
END;
/
Every time it is executed, it has to be parsed first. If we store this block in the database,
we only need to parse or compile it once (the first time), unless we make some changes,
or drop it. The following is an example of a procedure:
-- pro1.sql
-- Note that the file name and the procedure name are different.
SET SERVEROUTPUT ON
CREATE OR REPLACE PROCEDURE myproc1 IS
temp_emp_sal NUMBER(10,2); -- local variable, and its constraint is required..
BEGIN
SELECT emp_salary
INTO temp_emp_sal
FROM employee
WHERE emp_ssn = '999666666';
IF temp_emp_sal > 4000 THEN
DBMS_OUTPUT.PUT_LINE('Salary > 4000');
ELSE
DBMS_OUTPUT.PUT_LINE('Salary < 4000');
END IF;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Employee not found ');
END myproc1;
/
The keyword CREATE OR REPLACE directs the Oracle to create a new procedure, or
replace it if a procedure with same name already exists in the same schema. OR
REPLACE is optional. If the OR REPLACE is omitted, you have to drop the procedure
and load it back again every time you change it.
The following gives you an example of how to compile and execute the above procedure,
followed by its output:
SQL> @pro1
Procedure created.
The next thing to cover here is how to get values into and out of the procedure, we can do
this by defining variables in the implicit declaration section as IN, OUT or IN OUT. As
explained above, an IN variable is an input variable. An OUT variable is an output
variable, or a variable used to hold the value that will be returned to the user when the
program completes. An IN OUT variable serves as both. The following is an example
with IN and OUT variables:
-- pro2.sql
SET SERVEROUTPUT ON
CREATE OR REPLACE PROCEDURE myproc2
(temp_emp_ssn IN VARCHAR2,
temp_emp_sal OUT NUMBER)
IS
temp_sal number(10,2);
BEGIN
SELECT emp_salary
INTO temp_sal
FROM employee
WHERE emp_ssn = temp_emp_ssn;
IF temp_sal > 4000 THEN
DBMS_OUTPUT.PUT_LINE('Salary > 4000');
ELSE
DBMS_OUTPUT.PUT_LINE('Salary < 4000');
END IF;
temp_emp_sal := temp_sal;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('Employee not found ');
END myproc2;
/
The following gives you an example of how to compile and execute the above procedure,
followed by its output:
SQL> @pro2
Procedure created.
par_sal
----------
55000
A bind variable is a variable created in SQL*Plus and then referenced in other PL/SQL
subprograms. If you create a bind variable in SQL*Plus, you can use the variable like a
variable in your PL/SQL programs and then access the variable from SQL*Plus. In the
above example, variable par_sal serves this purpose, which is storing the return value.
A bind variable is prefixed with a “:” during the execution of the program, like
:par_sal
SQL>PRINT par_sal;
Or
Bear in mind that a procedure can have zero to many parameters, and can also return zero
to many values.
DROPPING A PROCEDURE
FUNCTIONS
A function is similar to a procedure, except it returns only one value. A function can
accept zero to many parameters, and it must return one and only one value. The data type
of the return value must be declared in the header of the function.
It is not necessary for the RETURN statement to appear in the last line of the execution
section, and there may also be more than one RETURN statement. Like procedure, OR
REPLACE is optional when creating a function.
An example follows:
-- func1.sql
CREATE OR REPLACE FUNCTION myfunc1
RETURN NUMBER
IS
ret_sal NUMBER(10,2);
BEGIN
SELECT emp_salary INTO ret_sal
FROM employee
WHERE emp_ssn = '999666666';
RETURN (ret_sal);
END myfunc1;
/
SQL> @func1
Function created.
par_sal
----------
55000
Like the procedure, we need to create a bind variable to hold the value returned from a
function. In the above example, a variable par_sal is created for that purpose.
-- func2.sql
CREATE OR REPLACE FUNCTION myfunc2(
i_dept_no NUMBER)
RETURN varchar2
IS
dept VARCHAR2(20);
BEGIN
SELECT dpt_name INTO dept
FROM department
WHERE dpt_no = i_dept_no;
RETURN (dept);
END myfunc2;
/
SQL> @func2
Function created.
PAR_DEP
--------------------------------
Admin and Records
DROPPING A FUNCTION
Oracle has some built-in functions, such as the numeric and aggregate functions. Please
refer to Chapter 10 of Oracle SQL for more information. Also rememberthat some Oracle
built-in functions, such as DECODE, cannot be used in PL/SQL program.
DATABASE TRIGGERS
A database trigger is a stored PL/SQL program unit associated with a specific database
table. ORACLE executes (fires) a database trigger automatically when a given SQL
operation (like INSERT, UPDATE or DELETE) affects the table. Unlike a procedure, or
a function, which must be invoked explicitly, database triggers are invoked implicitly.
You can associate up to 12 database triggers with a given table. A database trigger has
three parts: a triggering event, an optional trigger constraint, and a trigger action.
When an event occurs, a database trigger is fired, and an predefined PL/SQL block will
perform the necessary action. The owner, not the current user, must have appropriate
access to all objects referenced by the trigger action.
You cannot use COMMIT, ROLLBACK and SAVEPOINT statements within trigger
blocks. You have to be careful with using triggers as it may be executed thousands of
times for a large update, and therefore can seriously affect SQL execution performance.
The syntax for creating a trigger (the reserved words and phrases surrounded by brackets
are optional):
The reserved word CREATE specifies that you are creating a new trigger. The reserved
word REPLACE specifies that you are modifying an existing trigger. OR REPLACE is
optional.
The table_name is the name of the table associated with the trigger.
The clause, FOR EACH ROW, specifies a trigger is a row trigger and fires once for each
modified row.
Bear in mind that if you drop a table, all the associated triggers for the table are dropped
as well.
PARTS OF A TRIGGER
A database trigger has three parts, namely, a trigger statement, a trigger body and a
trigger restriction.
1. Trigger statement: The trigger statement specifies the DML statements like
UPDATE, DELETE and INSERT, and it executes the trigger body if the condition
is met.
2. Trigger Body: A trigger action is the procedure (PL/SQL block) that contains the
SQL or PL/SQL code to be executed for a triggering statement. Like stored
procedures, a trigger action can call SQL or PL/SQL statements, and define
PL/SQL language constructs (variables, constants, cursors, exceptions, and so on).
3. Trigger restriction: Restrictions on a trigger can be set using the WHEN clause, as
shown in the syntax for creating triggers. It basically specifies the condition under
which the trigger should be fired.
TYPES OF TRIGGERS:
The before/after options can be used to specify when the trigger body should be fired
with respect to the triggering statement. If the user indicates a BEFORE option, then
Oracle fires the trigger before executing the triggering statement. On the other hand, if an
AFTER is used, Oracle fires the trigger after executing the triggering statement.
A trigger may be a ROW or STATEMENT type. If the statement FOR EACH ROW is
present in the CREATE TRIGGER clause of a trigger, the trigger is a row trigger. A row
trigger is fired for each row affected by an triggering statement.
A statement trigger, however, is fired only once for the triggering statement, regardless of
the number of rows affected by the triggering statement.
The above example shows a trigger that limits the DML actions to the employee table to
weekdays from 8.30am to 6.30pm. If a user tries to insert/update/delete a row in the
EMPLOYEE table, a warning message will be prompted. As exemplified above, you can
customize error conditions via RAISE_APPLICATION_ERROR procedure to display an
error number (which must be between -2001 and -20999) and an appropriate error
message.
Self Note : Give some more examples which use Exceptions (pp 542, Pl/SQL book)
The following is an example of a row trigger:
The above trigger is used to keep track of all the transactions performed on the employee
table. If any employee is deleted, a new row containing the details of this employee is
stored in a table called xemployee. Similarly, if a new employee is inserted, a new row is
created in another table called nemployee, and so on.
Note that we can specify the old and new values of an updated row by prefixing the
column names with the :OLD and :NEW qualifiers.
The following is the output if we try to delete a record from the employee table. The
details of the deleted employee are automatically inserted into the xemployee table (You
must create the xemployee table first).
1 row deleted.
A trigger can be enables or disabled. An enabled trigger executes the trigger body if the
triggering statement is issued. By default, triggers are enabled. A disabled trigger does not
execute the trigger body even if the triggering statement is issued. We can disable a
trigger using the following syntax:
We can issue the following syntax to disable all the triggers associated with the table,
All triggers can be enabled for a specific table by using the following command
DROPPING TRIGGERS
The drop trigger command is used to drop a trigger from the database. The syntax is
SQL> DROP TRIGGER trigger_name;
PACKAGES
A package is a collection of PL/SQL objects grouped together under one package name.
Packages include procedures, functions, cursors, declarations, types, and variables.
All of the variables that you have used so far are local variables, meaning they are visible
only in the program in which they are declared. As soon as the program terminates, the
memory used to store the variable is freed, and the variable cannot be accessed again.
Sometimes it is necessary to have global variables, which can be shared among many
PL/SQL programs. A global variable is declared using the same syntax as is used for
declaring a local variable. While local variables are declared in the DECLARE section of
an individual program, global variables are declared in the DECLARE section of a
package.
PACKAGE SPECIFICATION
A package consists of a specification and a body. The package specification, also called
the package header, declares global variables, cursors, procedures and functions that can
be called or accessed by other program units.
Syntax:
To declare a procedure in a package, you must specify the procedure name, followed by
the parameters and variable types, using the following format:
To declare a function in a package, you must specify the function name, parameters and
return variable type as follows:
PACKAGE BODY
A package body contains the code for the programs declared in the package specification.
The package body is optional. A package may have only package declarations and no
programs.
Syntax:
An example follows:
To create a package body we now specify each PL/SQL block that makes up the package,
note that we are not creating these blocks separately (no CREATE OR REPLACE is
required for the procedure and function definitions).
The body of a package can contain the procedures declared in the package specification,
functions declared in the package specification, definitions of cursors declared in the
package specification, local procedures and functions not declared in the package
specification and local variables.
An example follows:
FUNCTION fun_id_is_good(
emp_id IN employee.emp_ssn%TYPE)
RETURN BOOLEAN
IS
v_id_cnt NUMBER;
BEGIN
SELECT COUNT(*)
INTO v_id_cnt
FROM employee
WHERE emp_ssn = emp_id;
RETURN (1 = v_id_cnt);
EXCEPTION
WHEN OTHERS THEN
RETURN FALSE;
END fun_id_is_good;
END pac_mag_emp;
/
SQL> @findname
CURSORS IN PACKAGES
As we have learned in Chapter 2, a cursor is used to populate the result set of a multi-row
query. However, a cursor is static because it is tied to a specific query inside a function or
procedure. To make a cursor more dynamic (i.e. reusable and sharable among different
procedures and functions), like placing a procedure or a function in a package, we can
use a cursor variable.
A cursor variable has datatype REF CURSOR. It is like a pointer in C language, and it
points to a query work area in which the result set is stored. Therefore, it can be passed
freely as a parameter to other subprograms.
To create a cursor variable, you have to define a REF CURSOR type, followed by a
cursor variable of that type.
DECLARE
TYPE item_val_cv_type IS REF CURSOR RETURN equipment%ROWTYPE;
item_cv IN OUT item_val_cv_type;
This following example demonstrates the use of REF Cursors. It prints the equipment
description for the equipment number assigned by the user.
-- pac_item_data.sql
CREATE OR REPLACE PACKAGE item_data AS
TYPE item_val_cv_type IS REF CURSOR RETURN equipment%ROWTYPE;
PROCEDURE open_item_cv (
item_cv IN OUT item_val_cv_type,
item_number IN varchar2);
--Note the cursor variable is IN OUT parameter that means it has some one in it after
--the procedure gets execited. It is this value which is inputed into the next preocedure.
PROCEDURE fetch_item_data(
item_cv IN item_val_cv_type, item_row OUT equipment%ROWTYPE);
END item_data;
/
CREATE OR REPLACE PACKAGE BODY item_data AS
PROCEDURE open_item_cv (
item_cv IN OUT item_val_cv_type,
item_number IN varchar2)
IS
BEGIN
-- here is where the cursor gets populated.
OPEN item_cv FOR
SELECT * FROM equipment WHERE eqp_no = item_number;
END open_item_cv;
PROCEDURE fetch_item_data (
item_cv IN item_val_cv_type,item_row OUT equipment%ROWTYPE)
IS
BEGIN
FETCH item_cv INTO item_row;
END fetch_item_data;
END item_data;
/
SET SERVEROUTPUT ON
DECLARE
-- declare a cursor variable
item_curs item_data.item_val_cv_type;
item_number equipment.eqp_no%TYPE;
item_row equipment%ROWTYPE;
BEGIN
item_number := '4321';
-- open the cursor using a variable
item_data.open_item_cv(item_curs,item_number);
-- fetch the data and display it
LOOP
item_data.fetch_item_data(item_curs,item_row);
EXIT WHEN item_curs%NOTFOUND;
DBMS_OUTPUT.PUT(item_row.eqp_no || ' ');
DBMS_OUTPUT.PUT_LINE(item_row.eqp_description);
END LOOP;
END;
/
SQL> @pac_item_data
Package created.
4321 Computer, PC
ADVANTAGES OF PACKAGES
1. Modularity - A package allows us to encapsulate related subprograms. As a result,
each package will help us to understand the concepts involved in an application in
a better manner
2. Easier application handling - To design the application, we must first specify the
objects or subprograms in the package specification. Once the specification has
been compiled, stored subprograms that refer to the package can be compiled.
Therefore, we need not define the package body completely, until we are ready
with the specification of the application. In other words, we can code and compile
a package specification without its body
3. Information hiding - We are aware of private and public objects. These objects
can be used to protect the integrity of the package. For example, consider that out
package consists of four subprograms, three public and one private subprogram.
The package hides the definition of the private subprogram so that only the
package (not out application) is affected if the definition changes. Thus, the
implementation details are hidden from other users, thereby protecting the
integrity of the package.
4. Added functionality - Public objects and cursors declared in the package
specification can be shared by all procedures that are executed in the environment
because they persist for the duration of a session.
5. Better performance - When we call a packaged program for the first time, the
whole package is loaded in the memory. Therefore, subsequent calls require no
disk input/output.
Summary: In this chapter, we have learned how to create, replace and drop a procedure,
function, triggers, and packages.
REVIEW EXERCISES
Concepts Quiz:
1. [easy] Write a function incr_salary, which takes two parameters, employee ssn and
the percentage by which the salary needs to be raised and returns the new salary.
2. [easy] Write a procedure Get_On_Hand that has one IN and one OUT parameter. This
procedure should take in equipment number and send the quantity on hand for this
equipment through the out parameter.
3. [easy] Write a procedure called emp_dept which has one IN and one OUT parameter.
This procedure should take in employee ssn and send the name of the department he
is working for by getting it from the department table.
4. [easy] Write a trigger, which checks the quantity on hand in the equipment table for
every update and displays an error message if the quantity on hand falls below 2.
5. [easy] Write a trigger, which fires before the update of employee table for each row
and checks if the new salary is below 20000, if it is then it raises an error.
6. [moderate] Write a trigger on the department table which fires after deleting a row
and stores the information of this department into a new table ‘xdept’ which has the
same structure as that of the department and an additional field called deldate which
stores the date the department is deleted.
7. [moderate] Write a package which has one procedure and one function in it. The
procedure should take an employees ssn and return his name and department number.
The function is to take employees ssn and a percentage by which his salary is to be
raised and raise his salary accordingly and return the new salary.
8. [easy] Create a procedure to find out the SSN of the dept manager for an employee.
In other words, write a procedure that takes an employee’s SSN as the input variable,
and return the SSN of his or her dept manager using the same variable. Return ‘-1’ if
no dept manager can be found.
9. [easy] The employees’ salary is to be classified as following:
Class A: <= 30000
Class B: > 30000 and <= 60000
Class C: > 60000
Create a function to return the class of a salary for an employee, given his or her SSN.
10. [moderate] Assuming each department is granted only 5000 dollar for purchasing
PCs, create a function to check if a new row can be added to the equipment table. In
other words, create a function that takes a dept_no, eqp_qty_on_hand and eqp_value
as the parameters, and returns a 0 if the total value (after adding the row) does not
exceed the allowance, and return –1 if it does.
11. [easy] Create a trigger to ensure that no department manager or employee starts on
Saturday and Sunday.
12. [moderate] Create a trigger to limit 5 assignments to one employee.
13. [moderate] Create two tables, LT_ASGN_HISTORY and ASGN_HISTORY, both
with the following columns:
14. [easy] Create a procedure that calculate the total work hours planned for an employee
given his or her ssn.