-- 18 --

The Basics of PL/SQL

Up to this point, I've presented SQL as a language without procedural capabilities. However, Oracle offers procedural language extensions to SQL through the PL/SQL language. PL/SQL is the basis for the following application logic elements:

The purpose of this chapter is to introduce the fundamental elements of PL/SQL. You should feel comfortable with the syntax and use of PL/SQL before you attempt to design stored procedures (Chapter 19, "Creating and Using Stored Procedures, Functions, and Packages") and database triggers (Chapter 23, "Enforcing Business Rules with Database Triggers"). This chapter addresses the features in PL/SQL version 2.1.



Tip

Don't use SQL*Plus to type in each line of a PL/SQL script. If you make a typo, SQL*Plus won't provide any feedback until it reads the / that terminates the PL/SQL block--;SQL*Plus passes the entire block to the PL/SQL engine only when the block is complete. The PL/SQL engine is a component of the Oracle RDBMS. Instead, use a text editor, such as Notepad, for developing your PL/SQL scripts. You can paste the script directly into SQL*Plus or invoke it with the START or @ command.


PL/SQL Is a Block-Structured Language

PL/SQL is a block-structured language with a syntax similar to the C programming language. In addition to supporting embedded SQL statements, PL/SQL offers standard programming constructs such as procedure and function declarations, control statements such as IF-THEN-ELSE and LOOP, and declared variables. A PL/SQL program consists of procedures, functions, or anonymous blocks. An anonymous block is an unnamed PL/SQL block that has no arguments and returns no value. Anonymous blocks are common in scripts that are executed in a SQL*Plus session.

From a top-level perspective, Figure 18.1 illustrates the structure of a PL/SQL block, which includes an optional declaration section, an executable section, and an optional section for handling PL/SQL and SQL exceptions and errors. *

Figure 18.1. Top-level structure of a PL/SQL block.

The following code is a simple anonymous PL/SQL block that generates some test data. The block, which is executed from a SQL*Plus script, inserts 100 rows into TEST_TABLE.

SQL> @c:\po7_book\chap17\fig1

SQL> drop table test_table;

Table dropped.

SQL>

SQL> create table test_table (

  2  record_number      int,

  3  current_date       date);

Table created.

SQL>

SQL> DECLARE

  2

  2  max_records CONSTANT int := 100;

  3  i           int := 1;

  4

  4  BEGIN

  5

  5  FOR i IN 1..max_records LOOP

  6

  6    INSERT INTO test_table

  7           (record_number, current_date)

  8    VALUES

  9           (i, SYSDATE);

 10

 10  END LOOP;

 11

 11  COMMIT;

 12  END;

/

PL/SQL procedure successfully completed.

The preceding script is an anonymous PL/SQL block because it has no name--;it isn't declared as a procedure, function, or package. A single SQL*Plus script contains all the lines.. The first two SQL commands drop the TEST_TABLE and then create it.

The PL/SQL block actually starts with the word DECLARE. The declaration section declares the constant max_records and the variable i, which serves as a counter. The beginning of the executable portion of the block is designated by BEGIN. The block contains a single FOR LOOP that inserts a row into TEST_TABLE while i is less than or equal to max_records. When the FOR LOOP completes, the transaction is committed. The last line of the script is a /, which causes SQL*Plus to submit the PL/SQL block to the PL/SQL engine.

Unless a PL/SQL compilation error occurs, the only feedback that SQL*Plus provides is the message PL/SQL procedure successfully completed. In Chapter 19 I'll show you how to generate diagnostics from PL/SQL so that you can see the progress of a PL/SQL subprogram's execution.

The Declaration Section

The declaration section of a PL/SQL block is optional. However, you must declare all variables and constants that are referenced in the PL/SQL statements. To include a declaration section in a PL/SQL block, begin the PL/SQL block with the word DECLARE. Each variable or constant declaration consists of its name, datatype, and an optional default value. As with all PL/SQL statements, each variable and constant declaration ends with a semicolon. Here are some examples of declared variables and constants:

Fax_Number             VARCHAR2(10);

Current_Used_Value     NUMBER(6,2) := 100.00;

Max_Current_Used_Value        REAL := 9999.99;

State                  VARCHAR2(2) := 'CA';

The Executable Section

The executable section of a PL/SQL block follows the keyword BEGIN. Each PL/SQL statement ends with a semicolon. These statements can be categorized as

The Exception Section

An exception is an error condition that occurs during the execution of a PL/SQL program. An exception might be predefined--;for instance, an INSERT statement attempts to add a duplicate row to a table, resulting in the DUP_VAL_ON_INDEX exception begin raised. You can also define exceptions that are specific to your application. The exception section defines the exception handlers invoked for both predefined and user-defined exceptions. Each exception handler consists of one or more PL/SQL statements.

Declaring Variables with PL/SQL

Unlike SQL, PL/SQL provides additional datatypes. In addition to the normal Oracle SQL datatypes, PL/SQL enables you to declare variables with these datatypes:

In addition, PL/SQL provides two composite datatypes: TABLE and RECORD. Chapter 20, "PL/SQL Datatypes and Variables," addresses the details of using these composite datatypes.

Using %TYPE to Declare a Variable

The syntax for declaring a variable with %TYPE is

variable-name   table-name.column-name%TYPE;

where column-name is a column defined in table-name.

For example, you declare a variable to store a repair depot technician's name in this way:

Tech_Name   Depot_Estimate.Technician%TYPE;


The benefit of using %TYPE in a variable declaration is that the PL/SQL code is dependent on the definition of the Technician column in the Depot_Estimate table.

Using %ROWTYPE to Declare a Variable

The syntax for declaring a variable with %ROWTYPE is

variable-name   table-name%ROWTYPE;

For instance, a composite variable that stores a row from the Depot_Estimate table is declared like this:

Depot_Est_Row   Depot_Estimate%ROWTYPE;

Here's how to access an element of Depot_Est_Row:

Depot_Est_Row.Technician := 'RICHARDSON';


Some Familiar Control Structures

Several PL/SQL statements control the flow of execution of a PL/SQL subprogram.

Before you can build stored procedures and triggers, you should be familiar with the basics of PL/SQL programming. The next part of this chapter explains how to use these statements.

The IF-THEN-ELSIF Statement

The syntax of PL/SQL's IF-THEN-ELSIF statement differs somewhat from that of the comparable statement in the C programming language. The PL/SQL syntax is

IF condition THEN

   statement; ...  statement;

[ELSIF condition THEN

statement; ...  statement;]

...

[ELSIF condition THEN

statement; ...  statement;]

[ELSE

statement; ...  statement;]

END IF;

where condition is a valid PL/SQL condition and statement is a valid PL/SQL statement.

Regarding the IF-THEN-ELSIF statement, be aware of the following:

Here's a simple example of the IF-THEN-ELSIF statement:

if MOD(i,5) = 0 then

  rec_number := 5;

elsif MOD(i,7) = 0 then

  rec_number := 7;

else

  rec_number := i;

end if;

The Simple LOOP Statement

The most basic type of loop is the LOOP statement without any additional qualifiers.

LOOP

  statement; ... statement;

END LOOP;

Obviously, this loop is an infinite loop. To exit this loop when a required condition is satisfied, use the EXIT statement.

The EXIT Statement

The EXIT statement has two forms:

The first form of EXIT causes control to exit the loop that encloses the EXIT statement.

The second form of EXIT causes control to exit the enclosing loop when the specified condition is met, as shown in this example:

SQL> declare

  2

  2  i  positive := 1;

  3  max_loops constant positive := 100;

  4

  4  begin

  5

  5  loop

  6

  6    i := i + 1;

  7    exit when i > max_loops;

  8

  8  end loop;

  9

  9  end;

 10  /

PL/SQL procedure successfully completed.

The WHILE-LOOP Statement

The WHILE-LOOP statement adds a condition to a loop.

WHILE condition LOOP

  statement; ... statement;

END LOOP;

Here's a simple example of a WHILE-LOOP statement:

WHILE I < 100 LOOP

  I := I + 1;

  insert into temp_table (rec_number) values (I);

END LOOP;

The FOR-LOOP Statement

The FOR-LOOP is quite similar to the WHILE-LOOP. The syntax is

FOR loop-variable IN [REVERSE] lower-bound..upper-bound LOOP

  statement;  ...  statement;

END LOOP;

where loop-variable is an integer variable that serves as a counter, lower-bound is the lower bound of the increment range, and upper-bound is the upper bound of the increment range.

REVERSE is an optional keyword that, if used, causes the loop to decrement from upper-bound to lower-bound.

Here is a simple example of a FOR-LOOP statement:

for i in 1..max_loops loop

  j := j + j;

  dbms_output.put_line('j: ' || to_char(j));

end loop;

The GOTO Statement

Yes, PL/SQL allows you to use the potentially dangerous GOTO statement. Of course, to use GOTO, you must provide a label to which control is transferred. In PL/SQL, a statement label is defined in this way:

<<my_label>>

Here's an alternative to the EXIT statement that uses GOTO and a label:

SQL> declare

  2

  2  i  positive := 1;

  3  max_loops constant positive := 100;

  4

  4  begin

  5

  5  i := 1;

  6

  6  loop

  7

  7    i := i + 1;

  8    if i > max_loops then

  9       goto more_processing;

 10    end if;

 11

 11  end loop;

 12

 12  <<more_processing>>

 13  i := 1;

 14

 14  end;

 15  /

PL/SQL procedure successfully completed.

The NULL Statement

For certain situations, you should indicate to PL/SQL that no action is to be taken. For instance, in an exception handler, you might not want to do anything when a particular exception occurs. For the sake of clarity, use the NULL statement in an IF-THEN-ELSIF to indicate that no action is to be taken for a particular ELSIF clause.

Unfortunately, Oracle chose to name this statement NULL, even though it has nothing to do with the null value. Here's an example of the NULL statement:

if (mod(i,10) = 0) then

  i := i + 1;

else

    NULL;

end if;

The Assignment Statement

As you've already seen, PL/SQL uses := to assign a value to a PL/SQL variable. You can define a constant's value or a variable's default value in the declaration section. One point is worth noting: You can't assign a NULL to a variable that was declared using the %TYPE notation when the referenced column is defined as NOT NULL.

Including Comments in a PL/SQL Subprogram

PL/SQL gives you two ways to document your code. First, you can add a comment on any line by placing a -- followed by the comment, as shown: ***Production: Please make sure the double hyphens in the paragraph above and the code below do not become em dashes. Thanks.***

Depot_Est_Row.Technician := Last_Tech_Name;

-- Assign the name of the last technician involved

You can also add comments in the C-style--;by enclosing them within /* and */. This method is best suited for including multiline comments:

j := j + 1;

/* The next section inserts a row into the Utility_Audit table

   to record the name of the current Oracle user and the

   current date and time (SYSDATE).

*/

insert into Utility_Audit

...

Using SQL Statements in a PL/SQL Program

You can use SQL statements in an anonymous block, procedure, or function as they are used in SQL*Plus, with a few differences. As with other PL/SQL statements, each SQL statement must end with a semicolon. However, PL/SQL enables you to reference declared variables in a SQL statement, as shown here:

DECLARE

max_records CONSTANT int := 100;

i           int := 1;

BEGIN

FOR i IN 1..max_records LOOP

  if (mod(i,10) = 0) then

    INSERT INTO test_table

           (record_number, current_date)

    VALUES

           (i, SYSDATE);

  else

    NULL;

  end if;

END LOOP;

COMMIT;

END;

/

In this example the INSERT statement uses the numeric variable i and the pseudocolumn SYSDATE to place values in the Record_Number and Current_Date columns.

PL/SQL and the SELECT Statement

In a PL/SQL subprogram, the SELECT statement employs another clause--;INTO--;to identify the PL/SQL variables that should receive column values. Place the INTO clause between the select list and the FROM clause. Here is an example of an anonymous PL/SQL block that contains a SELECT statement:

SQL> set serveroutput on

SQL>

SQL> declare

  2

  2  Average_Body_Temp     Patient.Body_Temp_Deg_F%type;

  3

  3  begin

  4

  4  dbms_output.enable;

  5

  5  select avg(Body_Temp_Deg_F)

  6    into Average_Body_Temp

  7    from Patient;

  8

  8  dbms_output.put_line('Average body temp in Deg. F: ' ||

     to_char(Average_Body_Temp,'999.99'));

  9

  9  end;

 10  /

Average body temp in Deg. F:   99.80

PL/SQL procedure successfully completed.

PL/SQL Subprograms

PL/SQL also supports the use of subprograms--;named procedures and functions. A PL/SQL procedure performs some action and can accept optional parameters. A PL/SQL function returns a value of some specified datatype and can also accept optional parameters.

Using Sub-Blocks

PL/SQL permits you to include sub-blocks within a block. For instance, this anonymous block contains another anonymous sub-block that has its own declaration section.

SQL> declare

  2

  2  max_i       constant int := 100;

  3  i           int := 1;

  4  rec_number  int;

  5

  5  begin

  6

  6  for i in 1..max_i loop

  7

  7    if mod(i,5) = 0 then

  8      rec_number := 5;

  9    elsif mod(i,7) = 0 then

 10      rec_number := 7;

 11    else

 12      rec_number := i;

 13    end if;

 14

 14    insert into test_table

 15      (record_number, current_date)

 16      values

 17      (rec_number, sysdate);

 18

 18  -- Here is a sub-block:

 19

 19    declare

 20    max_j constant int := 20;

 21    j int := 1;

 22

 22    begin

 23

 23      for j in 1..max_j loop

 24

 24        rec_number := rec_number * j;

 25

 25        insert into test_table

 26          (record_number, current_date)

 27          values

 28          (rec_number, sysdate);

 29

 29      end loop;

 30

 30    end;

 31

 31    commit;

 32  end loop;

 33

 33  end;

 34  /

PL/SQL procedure successfully completed.

SQL> select count(*) from test_table;

 COUNT(*)

---------

     2100



Tip

Although PL/SQL allows you to embed blocks within one another, this practice is not desirable for two reasons. First, it reduces the readability--;and the resulting maintainability--;of your code. Second, other PL/SQL subprograms cannot use embedded blocks. You should strive to design procedures and functions for improved code reuse and maintainability.


Declaring a Procedure

In addition to anonymous blocks, you can also declare PL/SQL procedures and functions. The syntax for declaring a procedure is

PROCEDURE procedure-name [(argument1 ... [, argumentN) ] IS

[local-variable-declarations]

BEGIN

executable-section

[exception-section]

END [procedure-name];

where procedure-name is the procedure name and subject to Oracle database object-naming restrictions.

argument1 through argumentN are optional argument declarations that consist of

argument-name [IN | OUT] datatype [ {:= | DEFAULT} value]

local-variable-declarations are optional declarations of variables, constants, and other procedures and functions local to procedure-name.

executable-section is the PL/SQL statements that compose the procedure.

exception-section is the optional exception-handling section of the procedure.



Note

The distinction between stored procedures and procedures that are declared and used in anonymous blocks is important. The procedures that are declared and called in anonymous blocks are temporal; when the anonymous block has completed execution, they no longer exist as far as Oracle is concerned. A stored procedure that is created with a CREATE PROCEDURE statement or contained in a package is permanent in the sense that it can be invoked by a SQL*Plus script, a PL/SQL subprogram, or a database trigger.


To illustrate this syntax, here is an example of an anonymous block that declares a procedure named Record_Patient_Temp_Deg_C. This procedure has two arguments: the patient ID and the patient's body temperature as measured in degrees Centigrade.

SQL> declare

  2

  2  New_Patient_ID  Patient.Patient_ID%type;

  3  High_Fever      constant real := 42.0;

  4

  4  procedure Record_Patient_Temp_Deg_C (Patient_ID varchar2,

  5                                       Body_Temp_Deg_C real) is

  6

6  Temp_Deg_F real;

  7

  7  begin

  8

  8    Temp_Deg_F := (9.0/5.0)*Body_Temp_Deg_C + 32.0;

  9

  9    insert into Patient

 10    (Patient_ID, Body_Temp_Deg_F)

 11    values

 12    (Patient_ID, Temp_Deg_F);

 13

 13    commit;

 14  end;

 15

 15  begin

 16

 16  New_Patient_ID := 'GG9999';

 17

 17  Record_Patient_Temp_Deg_C (New_Patient_ID, High_Fever);

 18

 18  end;

 19  /

PL/SQL procedure successfully completed.

SQL> select Patient_ID, Body_Temp_Deg_F

  2  from Patient

  3  where

  4  Patient_ID = 'GG9999';

PATIEN BODY_TEMP_DEG_F

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

GG9999           107.6

Notice that the variables declared within a procedure are not accessible outside the procedure.

SQL> declare

  2

  2  procedure Delete_Patients is

  3

  3  Temp_Deg_F real;

  4

  4  begin

  5

  5    delete from Patient

  6    where

  7    Patient_ID = 'GG3333';

  8

  8    commit;

  9

  9  end;

 10

 10  begin

 11

 11  Temp_Deg_F := 100.0;

 12

 12  end;

 13  /

Temp_Deg_F := 100.0;

*

ERROR at line 11:

ORA-06550: line 11, column 1:

PLS-00201: identifier 'TEMP_DEG_F' must be declared

ORA-06550: line 11, column 1:

PL/SQL: Statement ignored

For further information on the use of stored procedures, please refer to Chapter 19.

Declaring a Function

A PL/SQL function declaration is similar to a procedure declaration--;except that the function returns a value of a predefined datatype. The syntax for declaring a function is

FUNCTION function-name [(argument1 ... [, argumentN) ]

RETURN function-datatype IS

[local-variable-declarations]

BEGIN

executable-section

[exception-section]

END [function-name];

where function-name is the function name and subject to Oracle database object-naming restrictions.

argument1 through argumentN are optional argument declarations that consist of

argument-name [IN | OUT] datatype [ {:= | DEFAULT} value]

function-datatype is the datatype returned by function-name.

local-variable-declarations are optional declarations of variables, constants, and other procedures and functions local to function-name.

executable-section is the PL/SQL statements that compose the function

exception-section is the optional exception-handling section of the function.

For instance, a function that returned the highest patient temperature expressed in degrees Centigrade via an argument would be declared in this way:

SQL> declare

  2

  2  Pat_ID     Patient.Patient_ID%type;

  3  High_Fever real;

  4  status     boolean;

  5

  5  function Highest_Fever_Temp_Deg_C (Patient_ID OUT varchar2,

  6                                     Body_Temp_Deg_C OUT real)

  7           return boolean is

  8

  8  Max_Fever_Deg_F real;

  9

  9  begin

 10

 10  select Patient_ID, Body_Temp_Deg_F

 11    into Patient_ID, Max_Fever_Deg_F

 12    from Patient

 13    where

 14    Body_Temp_Deg_F =

 15    (select max(Body_Temp_Deg_F)

 16     from Patient);

 17

 17  Body_Temp_Deg_C := (Max_Fever_Deg_F - 32.0)*5.0/9.0;

 18

 18  return true;

 19

 19  end;

 20

 20  --  Beginning of executable section of anonymous block.

 21

 21  begin

 22

 22  dbms_output.enable;

 23

 23  status := Highest_Fever_Temp_Deg_C (Pat_ID, High_Fever);

 24

 24  dbms_output.put_line('Patient ' || Pat_ID || ' has a fever of ' ||

 25                       to_char(High_Fever,'999.9') || ' C. ');

 26

 26  end;

 27  /

Patient GG9999 has a fever of   42.0 C.

PL/SQL procedure successfully completed.

Procedure and Function Arguments

You can optionally define each procedure and function argument as one of the following:

The next example uses these three types of arguments:

SQL> declare

  2

  2  This_Arg1 number;

  3  This_Arg2 number;

  4  This_Arg3 number;

  5

  5  procedure Different_Arguments

  6            (arg1 IN     number,

  7             arg2 OUT    number,

  8             arg3 IN OUT number) is

  9

  9  begin

 10

 10  arg2 := arg1;

 11  arg3 := arg3 + 1;

 12

 12  end;

 13

 13  --  Beginning of executable section of anonymous block.

 14

 14  begin

 15

 15  This_Arg1 := 3.14159;

 16

 16  Different_Arguments (This_Arg1, This_Arg2, This_Arg3);

 17

 17  end;

 18  /

PL/SQL procedure successfully completed.

In addition, a default value can be defined for an argument.

procedure Different_Arguments

          (arg1 IN     number := 100,

           arg2 OUT    number,

           arg3 IN OUT number) is

Summary

This chapter covers the essential elements of PL/SQL.