Infolinks

Wednesday 20 February 2013

PL/SQL CHAPTER 3



CHAPTER 3

PROCEDURES, FUNCTIONS, TRIGGERS AND PACKAGES

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

OBJECTIVES

In this chapter, we will briefly cover the following topics:

  • 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

PROCEDURES AND FUNCTIONS

A procedure or function is a named PL/SQL block. Procedures and functions are normally stored in the database within package specifications (which is a wrapper for a group of named blocks), although they may also be stored in the database individually. A procedure or function is parsed or compiled at the time it is stored. As a result, they tend to run faster compared to non-procedural SQL scripts, which require extra time for compilation.

A procedure or function can be invoked from most Oracle tools like SQL*Plus, or other programming languages like C++ and JAVA.

Benefits of using procedures and functions include:

  1. Improved data security and integrity.
·         Control indirect access to objects from non-privileged users.
·         Ensure that related actions are performed together, or not at all, by funnelling actions for related tables through a single path.

  1. 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.

  1. 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. 
 
Syntax for creating a procedure:

CREATE OR REPLACE PROCEDURE <procedure name> (<parameter1 name> <mode> <data type>,     <parameter1 name> <mode> <data type>,  …) IS
<Variable declarations>
BEGIN
Executable Commands
EXCEPTION
Exception handlers
END.

OR REPLACE - This option re-creates the procedure, maintaining the privileges previously granted.

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:

CREATE OR REPLACE PROCEDURE pro_sample(emp_ssn NUMBER(2), …..)

Instead, you should do this:

CREATE OR REPLACE PROCEDURE pro_sample(emp_ssn NUMBER, …..)

Parameters can have three modes:

Mode

Description

IN
This type of parameter is passed to the procedure as a read-only value that cannot be changed within the procedure.
OUT
This type of parameter is write-only, and can only appear on the left side of an assignment statement in the procedure.
IN OUT
Combination of IN and OUT; the parameter of this mode is passed to the procedure, and its value can be changed within the procedure.

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:

Syntax for compiling a procedure:

SQL>@<SQL filename>

or 
SQL>start <SQL filename>

in which <SQL filename> is an .sql file which contains your procedure.

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:

SQL> show errors;

After you compile a procedure, you can execute it.

Syntax for executing a procedure:

SQL> EXECUTE <procedure name> or EXEC <procedure name>

Let’s look at the following unnamed block:

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.

SQL> exec myproc1
Salary > 4000

PL/SQL procedure successfully completed.


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.

SQL> var par_sal number;

SQL> exec myproc2('999666666', :par_sal);
Salary > 4000

PL/SQL procedure successfully completed.

SQL> print par_sal;

par_sal
----------
     55000

USING BIND VARIABLES TO CHECK THE RESULT


In the above example, notice that a variable called par_sal is created after the procedure is compiled. Remember the program we created above takes two parameters, one IN variable and one OUT variable? The variable par_sal is created to hold the input passed to the procedure or output value returned from the program. It is actually called a bind variable in Oracle.

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

To check the value of a bind variable, simply use:

SQL>PRINT par_sal;

Or

SQL>SELECT par_sal FROM dual;

Bear in mind that a procedure can have zero to many parameters, and can also return zero to many values.

DROPPING A PROCEDURE


As mentioned above, a procedure needs to be loaded before it can be executed. Once it is loaded, it can also be unloaded, or dropped.

The syntax for dropping a procedure:

SQL>DROP PROCEDURE <procedurename>;

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.

Syntax for creating a function:

CREATE OR REPLACE FUNCTION <function name> (<parameter1 name> <mode> <data type>, <parameter1 name> <mode> <data type>,…)
            RETURN <function return value data type> IS
<Variable declarations>
BEGIN
Executable Commands
RETURN (return value);
EXCEPTION
Exception handlers
END.

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

The output of the above example is:

SQL> @func1

Function created.

SQL> var par_sal varchar2(20);
SQL> EXECUTE :par_sal := myfunc2;

PL/SQL procedure successfully completed.

SQL> print par_sal;

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.

The following is an example of a function that accepts a parameter:

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

The output of the above example is:

SQL> @func2

Function created.

SQL> var par_dep varchar2(20);
SQL> EXECUTE :par_dep := myfunc2(3);

PL/SQL procedure successfully completed.

SQL> print par_dep

PAR_DEP
--------------------------------
Admin and Records


DROPPING A FUNCTION


Syntax for dropping a function:

SQL>DROP FUNCTION <functionname>;

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.

Database triggers can be used to perform any of the following:
·      Audit data modification
·      Log events transparently
·      Enforce complex business rules
·      Derive column values automatically
·      Implement complex security authorizations
·      Maintain replicate tables

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):

CREATE [OR REPLACE] TRIGGER trigger_name
{BEFORE|AFTER} triggering_event ON table_name
[FOR EACH ROW]
[WHEN condition]
DECLARE
Declaration statements
BEGIN
Executable statements
EXCEPTION
Exception-handling statements
END;

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 trigger_name references the name of the trigger.

BEFORE or AFTER specify when the trigger is fired (before or after the triggering event).

The triggering_event references a DML statement issued against the table.

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.

A WHEN clause specifies the condition for a trigger to be fired.

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:

Triggers may be called BEFORE or AFTER the following events:

INSERT, UPDATE and DELETE.

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 following is an example of a statement trigger:

CREATE OR REPLACE TRIGGER mytrig1
BEFORE DELETE OR INSERT OR UPDATE ON employee
BEGIN
     IF (TO_CHAR(SYSDATE, 'day') IN ('sat', 'sun')) OR
     (TO_CHAR(SYSDATE,'hh:mi') NOT BETWEEN '08:30' AND '18:30') THEN
           RAISE_APPLICATION_ERROR(-20500, 'table is secured');
     END IF;
END;
/

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:

CREATE OR REPLACE TRIGGER mytrig2
AFTER DELETE OR INSERT OR UPDATE ON employee
FOR EACH ROW
BEGIN  
     IF DELETING THEN     
          INSERT INTO xemployee (emp_ssn, emp_last_name,emp_first_name, deldate)
          VALUES (:old.emp_ssn, :old.emp_last_name,:old.emp_first_name, sysdate);   
     ELSIF INSERTING THEN
          INSERT INTO nemployee (emp_ssn, emp_last_name,emp_first_name, adddate)
          VALUES (:new.emp_ssn, :new.emp_last_name,:new.emp_first_name, sysdate);
     ELSIF UPDATING('emp_salary') THEN
          INSERT INTO cemployee (emp_ssn, oldsalary, newsalary, up_date)
          VALUES (:old.emp_ssn,:old.emp_salary, :new.emp_salary, sysdate);
     ELSE
          INSERT INTO uemployee (emp_ssn, emp_address, up_date)
          VALUES (:old.emp_ssn, :new.emp_address, sysdate);  
     END IF;
END;
/

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).

SQL>  DELETE FROM  employee WHERE emp_last_name = 'Joshi';

1 row deleted.

SQL> SELECT * FROM xemployee;

EMP_SSN   EMP_LAST_NAME   EMP_FIRST_NAME DELDATE
-------------   -------------------------    -------------------------- -----------------
999333333  Joshi                              Dinesh                         02-MAY-03


ENABLING AND DISABLING TRIGGERS


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:

SQL>ALTER TRIGGER trigger_name DISABLE;

We can issue the following syntax to disable all the triggers associated with the table,

SQL>ALTER TABLE table_name DISABLE ALL TRIGGERS;

To enable a trigger, which is disabled, we can use the following syntax:

SQL>ALTER TABLE table_name ENABLE trigger_name;

All triggers can be enabled for a specific table by using the following command

SQL> ALTER TABLE table_name ENABLE ALL TRIGGERS;

 

DROPPING TRIGGERS

The drop trigger command is used to drop a trigger from the database. The syntax is

SQL> DROP TRIGGER trigger_name;


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:


PACKAGE <package name>
IS
            <variable declarations>;
            <cursor declarations>;
            <procedure and function declarations>;
END <package name>;

To declare a procedure in a package, you must specify the procedure name, followed by the parameters and variable types, using the following format:

PROCEDURE <procedure name> (param1 param1datatype, param2 param2datatype,…);

To declare a function in a package, you must specify the function name, parameters and return variable type as follows:

FUNCTION <function name> (param1 param1datatype, param2 param2datatype,…)
RETURN <return data type>;

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:

PACKAGE BODY<package name>
IS
            <cursor specifications>
            <module bodies>
END <package name>;

To create a package specification we use a variation on the CREATE command; all we need to put in the specification is each PL/SQL block header that will be public within the package. Procedures, functions, cursors, and variables that are declared in the package specification are global. They can be called, or used by external users that have the EXECUTE permission for the package, or that have EXECUTE ANY PROCEDURE privileges.

An example follows:

CREATE OR REPLACE PACKAGE pac_mag_emp
AS
PROCEDURE pro_find_ename(
     emp_id IN employee.emp_ssn%TYPE,
     employee_first_name OUT employee.emp_first_name%TYPE,
     employee_last_name OUT employee.emp_last_name%TYPE);
FUNCTION fun_id_is_good(
     emp_id IN employee.emp_ssn%TYPE)
     RETURN BOOLEAN;
END pac_mag_emp;
/

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:

CREATE OR REPLACE PACKAGE BODY pac_mag_emp
AS
PROCEDURE pro_find_ename(
     emp_id IN employee.emp_ssn%TYPE,
     employee_first_name OUT employee.emp_first_name%TYPE,
     employee_last_name OUT employee.emp_last_name%TYPE)
IS
BEGIN
     SELECT emp_first_name,emp_last_name
     INTO employee_first_name, employee_last_name
     FROM employee
     WHERE emp_ssn = emp_id;
EXCEPTION
     WHEN OTHERS THEN
          DBMS_OUTPUT.PUT_LINE('Error in finding employee ssn: '||emp_id);
END pro_find_ename;

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

CALLING STORED PACKAGES


To reference a variable, procedure or a function declared in the package specification, uswe the following notation:

<PACKAGE NAME>.<SUBPROGRAM NAME>

The following is an example of calling a procedure from another program:

-- findname.sql
SET SERVEROUTPUT ON
DECLARE
     var_first_name      employee.emp_first_name%type;
     var_last_name       employee.emp_last_name%type;
BEGIN
      pac_mag_emp.pro_find_ename(‘999666666’,var_first_name, var_last_name);
      DBMS_OUTPUT.PUT_LINE('The value returned is ' || var_first_name || var_last_name);
END;
/

The output of the above example is:

SQL> @findname

The value returned is BijoyBordoloi

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.

The syntax for defining a REF CURSOR type:

 TYPE ref_type_name IS REF CURSOR [RETURN <return_type>];

where <return_type> represents a row in a database table.

An example of cursor variable:

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

The output of the above example is:

SQL> @pac_item_data

Package created.


Package body created.

4321  Computer, PC

PL/SQL procedure successfully completed.


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

Learn Theses Terms

1.      Procedure: Procedures are simply named PL/SQL blocks.
2.      Functions: A function is similar to a procedure, except that when it is called, it returns a single value that is assigned to a variable.
3.      IN: Parameter is passed to the procedure as a read-only value that cannot be changed with in the procedure.
4.      OUT: Parameter is a write-only value that can only appear on the left side of an assignment statement in the procedure.
5.      IN OUT: Combination of IN and OUT; the parameter is passed to the procedure, and its value can be changed within the procedure.
6.      Package body: The package body contains the code for the programs declared in the package specification.
7.      Package specification: 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.
8.      Trigger: A trigger is PL/SQL code block attached and executed by an event, which occurs to a database table.
9.      Row Trigger: A row trigger is fired as many times as there are rows affected by the triggering statement.
10.  Statement Trigger: A statement trigger is fired once for the triggering statement. In other words, a statement trigger fires once, regardless of the number of rows affected by the triggering statement.

Concepts Quiz:

1.      What is the distinguishing characteristic that makes functions different from procedures? (check all that apply)
a.       Functions require a PRAGMA RESTRICT clause.
b.      Functions only take IN parameters.
c.       Functions are stored in the database.
d.      Functions require a return value.
e.       None of the above.
2.      What statement(s) will cause control to return to the calling environment in a function?
a.       The raising of an exception.
b.      The initialization of an OUT parameter.
c.       Writing to a database table.
d.      The RETURN statement.
e.       None of the above.
3.      If a procedure has an IN parameter, then it must have an OUT parameter.
a.       True
b.      False
4.      If a function declares a user-defined exception but never explicitly raises the exception, which of the following will be true?
a.       The function will not be able to compile.
b.      The function will fail a purity level check.
c.       The exception will never be raised.
d.      As long as the exception has a RETURN clause, there is no error in having a user-defined exception and not calling it.
5.      IN OUT parameters are permissible in functions.
a.       True
b.      False
c.       The function will compile with an IN OUT parameter, but it is not advisable to use them.
6.      Assume a trigger named mytrig1 already exists in the database. If you use the CREATE clause to modify the existing trigger, what error message is generated? Explain your answer.
7.      All procedures and functions in a package body must be declared in the package specification.
a.       True
b.      False
8.      The main advantages to grouping procedures and functions into packages are as follows (check all that apply):
a.       It follows the trendy object method of programming.
b.      It is a more efficient way of utilizing the processor memory.
c.       It makes greater use of the security privileges of various users.
d.      It is a more efficient method to maximize tablespace storage.
e.       It keeps you on good terms with the DBA.
9.      The package specification is merely a formality for other programmers to let them know what parameters are being passed in and out of the procedures and functions. It hides the program logic but in actuality it is not necessary and is incorporated into the package body.
a.       True
b.      False
10.  A trigger can fire for which of the following?
a.       Before a triggering event.
b.      After a triggering event.
c.       Before or after a triggering event.
11.  How many times does a row trigger fire if a DML (INSERT, UPDATE, or DELETE) operation is issued against a table?
a.       As many times as there are rows affected by the DML operation.
b.      Once per DML operation.
12.  How many times does a statement trigger fire if a DML (INSERT, UPDATE, or DELETE) operation is issued against a table?
a.       As many times as there are rows affected by the DML operation.
b.      Once per DML operation.


Coding Exercises and Questions

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:

Column Name
Datatype
Size
Comments
work_emp_ssn
CHAR
9
Primary Key.  Employee social security  number.  Also Composite Foreign  Key link to assignment table
work_pro_number
NUMBER
2
Primary Key.  Project number.  Also Composite Foreign  Key link to assignment table.
work_hours_completed
NUMBER
(5,1)
Number of hours an employee has completed on a project.
date_last_updated
DATE

Primary Key. Date when this row is changed.

Create a trigger that does the following:
When the work_hours_planned becomes 0, (not NULL, assuming that a work_hours_planned with NULL values means it is not assigned yet, while work_hours_planned  = 0 means the assignment has been completed),
a.       If the total work hours exceed 100, insert a new record to LT_ASGN_HISTORY (long term assignment history).
b.      Else insert a new record to ASGN_HISTORY.

14. [easy] Create a procedure that calculate the total work hours planned for an employee given his or her ssn.


1 comment: