You are on page 1of 18

PL/SQL

rocedural Language for Structured Query Language (PL/SQL) has many uses. Entire books have been devoted to PL/SQL alone. This chapter contains many examples of PL/SQL code and discusses the basic construction techniques of writing PL/SQL programs. PL/SQL has been a part of the Oracle database engine for many years. As its name implies, PL/SQL gives you a procedural backdrop in which to couch your SQL commands.
CrossReference

18
C H A P T E R

In This Chapter
Writing PL/SQL code Using declared variables Handling errors Using PL/SQL to create HTML Creating cursors Examining loop and IF statements

Other chapters in this book discuss techniques for working with PL/SQL. The Procedures, Packages, Functions, and Triggers section in Chapter 19 contains specific instructions and examples to construct the four important kinds of Objects using PL/SQL. Chapter 25 has an example PL/SQL procedure that uses Oracle8 Objects. Each section in this chapter has sample code that runs using the sample Tables included on the CD-ROM. Follow the instructions in Appendix B to install the Tables in your database.

On the CD-ROM

Writing PL/SQL Code


The basic structure of a PL/SQL follows:
DECLARE declare block BEGIN body block EXCEPTION exception block END;

The declare block contains definitions of variables and cursors. The body block contains the executable code you can break this code into subblocks. The exception block contains error handling code executed only if you encounter a predefined exception.

444

Chapter 18 3 PL/SQL

The program can be simple and short. In fact, the program body is the only required part; all other parts are optional. PL/SQL is used inside the database in the form of database Triggers. You can write application Triggers using PL/SQL in Oracles Forms 4 Package. Database design tools such as Oracle Case and Logic Works ERwin generate PL/SQL code for you to enforce business rules and Constraints. You can write and execute PL/SQL within SQL*Plus or SQL Worksheet. PL/SQL can execute SELECT, INSERT, UPDATE, or DELETE commands. Data Definition Language (DDL) commands are illegal in PL/SQL. There are special ways around this restriction, using Dynamic SQL and the DBMS_SQL Package, but I do not cover these techniques in this book. The only other SQL commands Oracle8 allows within a PL/SQL program are transaction control statements such as COMMIT and ROLLBACK. In addition to SQL commands, you can create and assign variables, perform
IF-THEN-ELSE logic, use loop and GOTO structures, and make comparisons.

Declared Variables
You can declare variables in two ways:doeff8returri5tytvghjg 3 Explicit. Declare explicit variables in the DECLARE block of the program. The syntax of the explicit variable declaration follows:
variable_name datatype [CONSTANT] [NOT NULL] [ [:= | DEFAULT] initvalue] ;

Replace variable_name with the actual variable name. Replace datatype with the actual datatype of the variable (such as VARCHAR2). Replace the optional parameter initvalue with a starting value for the variable. Use the CONSTANT parameter when a variable is assigned a value and never changes. For example, the following command declares a variable named LIBRARY_CARD_ID, which has a null value as the initial value:
LIBRARY_CARD_ID VARCHAR2(20);

The following command declares a variable named BOOKS_OUT, which is initialized to zero:
BOOKS_OUT NUMBER := 0;

3 Implicit. Use an assignment in the BODY BLOCK of the program to create an implicit variable. When you fetch a cursor using the cursor FOR LOOP, you

Chapter 18 3 Declared Variables

445

define an implicit variable for the record. See the following Cursors section for example code. The following simple PL/SQL program is a Trigger for adding the current date and time to a Column in a database:
-- trigger_one.sql CREATE OR REPLACE TRIGGER SEAPARK.BEF_INS_TANK BEFORE INSERT ON SEAPARK.TANK REFERENCING OLD AS old NEW AS new FOR EACH ROW BEGIN DECLARE TODAY_DATE DATE; BEGIN SELECT SYSDATE INTO TODAY_DATE FROM DUAL; :new.LAST_CHANGE_DATE := TODAY_DATE; END; END;

The variable :new.LAST_CHANGE_DATE is an implicit variable representing the value to be inserted into the Column LAST_CHANGE_DATE in the Table TANK.
Tip

You can compare the value of a Column prior to an update when you use the :old prefix on a Column in a Trigger that fires before an update. For example, the following Trigger checks the value of APPROVED_FLAG. If APPROVED_FLAG has been changed to Y, a date is added to the APPROVED_DATE Column and the Oracle User is added to the APPROVED_BY_NAME Column.
--trigger_two.sql CREATE OR REPLACE TRIGGER SEAPARK.BEF_UPD_TANK BEFORE UPDATE ON SEAPARK.TANK REFERENCING OLD AS old NEW AS new FOR EACH ROW BEGIN DECLARE TODAY_DATE DATE; BEGIN SELECT SYSDATE INTO TODAY_DATE FROM DUAL; IF :new.APPROVED_FLAG = Y and ( NVL(:old.APPROVED_FLAG, ?) <> Y ) THEN :new.APPROVED_DATE := TODAY_DATE; :new.APPROVED_BY_NAME := User; END IF; END; END; / show errors

The last two lines enable you to compile and view any errors when using SQL*Plus or SQL Worksheet to create and execute your PL/SQL commands.

446

Chapter 18 3 PL/SQL

IF-THEN-ELSE
The IF-THEN-ELSE phrase enables you to evaluate variables and take actions based on the values found. The general syntax follows:
IF if_condition THEN if block | NULL; [ ELSIF elsif_condition THEN elsif_block | NULL; ]... [ELSE else_condition THEN else_block | NULL; ] END IF;

Replace if_condition with a condition you can evaluate to TRUE or FALSE. Replace if_block with a set of valid PL/SQL commands. If you want Oracle8 to do nothing at this point, use the NULL; command. The ELSIF phrase is optional and you can repeat it as many times as needed. Replace elsif_condition with a condition you can evaluate to TRUE or FALSE. Replace elsif_block with a set of valid PL/SQL commands. If you want Oracle8 to do nothing at this point, use the NULL; command. The ELSE phrase is an optional catchall that shows actions completed when the conditions evaluated in the IF and ELSIF (if any) phrases fail. The following simple example shows how to take three different actions depending on the value of the ACCOUNT_BALANCE variable:
IF ACCOUNT_BALANCE < 0 THEN -- negative balance ACCOUNT_STATUS := Overdrawn; ELSIF ACCOUNT_BALANCE = 0 THEN -- zero balance ACCOUNT_STATUS := Zero Balance; ELSE -- positive balance ACCOUNT_STATUS := In the Black; END IF;

I use IF commands in many of this chapters examples.

Cursors
Use cursors to get information from the database and bring it into the PL/SQL program. A cursor contains an SQL SELECT command. There are two kinds of cursors:

Chapter 18 3 Cursors

447

3 Explicit. The explicit cursor requires a declaration statement. In addition, you must program the cursor to open, fetch data, and close. 3 Implicit. The implicit cursor is stated and executed directly in the program body. No special handling is required. Only SQL commands that return a single row or return no rows are allowed in an implicit cursor, however. INSERT, UPDATE, and DELETE commands contain implicit cursors.

Explicit cursors
The general syntax for an explicit cursor is:
DECLARE CURSOR cursorname IS query ;

Replace cursorname with the actual cursor name. In the body of the program, you must follow these three steps to use the cursor: 1. Open the cursor. The general syntax for opening a cursor follows:
OPEN cursorname;

2. Fetch data one row at a time. The general syntax for fetching (retrieving) one row follows:
FETCH cursorname INTO Variablename1, Variablename2, ...;

To retrieve another row, issue another FETCH command. Do not close the cursor until you complete all the rows. (Here is where a loop comes in handy.) 3. Close the cursor. The general syntax for closing a cursor follows:
CLOSE cursorname;
Example

For example, the purpose of the following PL/SQL program is to create new CHECKUP_HISTORY records for animals in the AQUATIC_ANIMAL Table, depending on their age. If a CHECKUP_HISTORY record already exists of the appropriate type, no row is inserted. The code follows:
- -cursor_one.sql DECLARE V_ID_NO NUMBER; V_AGE NUMBER; V_TOTAL_AGE NUMBER := 0; CURSOR ANIMAL IS SELECT ID_NO, TRUNC(SYSDATE) - TRUNC(BIRTH_DATE) FROM AQUATIC_ANIMAL; BEGIN -- open cursor now. OPEN ANIMAL; -- fetch first row now.

448

Chapter 18 3 PL/SQL

FETCH ANIMAL INTO V_ID_NO, V_AGE; V_TOTAL_AGE := V_TOTAL_AGE + - - fetch second row now. FETCH ANIMAL INTO V_ID_NO, V_AGE; V_TOTAL_AGE := V_TOTAL_AGE + - - fetch third row now. FETCH ANIMAL INTO V_ID_NO, V_AGE; V_TOTAL_AGE := V_TOTAL_AGE + - - fetch fourth row now. FETCH ANIMAL INTO V_ID_NO, V_AGE; V_TOTAL_AGE := V_TOTAL_AGE + - - close cursor now. CLOSE ANIMAL; END;

V_AGE; V_AGE; V_AGE; V_AGE;

Mark comments with a double dash (- -) or with slash asterisk opening and closing markers (/* */). Mixing cursors with loops is the easiest way to handle repeating processes you perform on each fetched row. See the following Loop section in this chapter for more information.

Implicit cursor
The syntax of an implicit SELECT cursor is:
SELECT column1, column2, ... INTO variable1, variable2, ... FROM ...

Replace column1, column2, ... with valid Columns or expressions as you use in any SELECT command. Replace variable1, variable2, ... with variables declared in the PL/SQL program. Each variable must match its corresponding Column in datatype and length. In addition to the preceding SELECT syntax, a valid INSERT, UPDATE, or DELETE command can also appear as an implicit cursor. Remember these two important points: 3 An implicit cursor appears in the body rather than in the declare block of the PL/SQL program. 3 An implicit SELECT cursor must return a single row. If the query returns more than one row, you must either change the query or use an explicit cursor.
Example

The following simple example contains an implicit cursor. The query uses a summary function and returns a single row and one Column. The value in the Column is assigned to a variable in the program.
-- cursor_two.sql

Chapter 18 3 Cursors

449

DECLARE V_TOTAL_AGE NUMBER; BEGIN implicit cursor here. SELECT SUM(TRUNC(SYSDATE) - TRUNC(BIRTH_DATE)) INTO V_TOTAL_AGE FROM AQUATIC_ANIMAL; END;

The next section shows several variations on using loop constructions in PL/SQL.

Cursor attributes
Four attributes inform you of your cursors state. Each attribute references a specific cursor by concatenating the cursor name onto the attribute name. You can only check cursor attributes (except the %ISOPEN attribute) while a cursor is open. The four attributes follow: 3 cursorname%FOUND. This attribute is TRUE when a row is fetched by the last FETCH command. If a FETCH is attempted but no row is returned (such as when you are at the end of a Table or when the query returns no rows) then %FOUND is FALSE. 3 cursorname%NOTFOUND. This attribute is the opposite of FOUND. This attribute is FALSE when a row is fetched by the last FETCH command. If a FETCH is attempted but no row is returned then %FOUND is TRUE. In an UPDATE statement, SQL%NOTFOUND is TRUE if no rows are updated. (SQL is the name of the implicit cursor.) 3 cursorname%ISOPEN. This attribute tells you if a cursor is open or closed. %ISOPEN is TRUE when the cursor has been opened and while FETCH commands are issued. %ISOPEN is FALSE before the cursor is opened and after it is closed. 3 %ROWCOUNT. This attribute gives you the number of rows fetched so far. Before any Rows are fetched, %ROWCOUNT is zero. If a cursor fetches three rows, %ROWCOUNT for that cursor is three. In an UPDATE statement, SQL%ROWCOUNT is the number of rows updated. If no rows are updated, SQL%ROWCOUNT is zero. (SQL is the name of the implicit cursor.) The following example tests the cursor attributes:
- - cursor_three.sql DECLARE V_ID_NO NUMBER; V_AGE NUMBER; V_TOTAL_AGE NUMBER := 0; CURSOR ANIMAL IS SELECT ID_NO, TRUNC(SYSDATE) - TRUNC(BIRTH_DATE) FROM AQUATIC_ANIMAL;

450

Chapter 18 3 PL/SQL

BEGIN - - open cursor now. OPEN ANIMAL; -- fetch first row now. FETCH ANIMAL INTO V_ID_NO, V_AGE; IF ANIMAL%FOUND THEN V_TOTAL_AGE := V_TOTAL_AGE + V_AGE; ELSE GOTO CLOSE_THE_CURSOR; END IF; -- fetch second row FETCH ANIMAL INTO V_ID_NO, V_AGE; IF ANIMAL%ROWCOUNT = 2 THEN V_TOTAL_AGE := V_TOTAL_AGE + V_AGE; ELSE GOTO CLOSE_THE_CURSOR; END IF; <<CLOSE_THE_CURSOR>> -- close cursor now. CLOSE ANIMAL; END;

The preceding example has two additional features: 3 Label. Enclose a label in double less than and greater than signs (<<LABELHERE>>). A label must have at least one command below it. Labels are referenced in a GOTO statement. 3 GOTO. The GOTO command enables you to jump to another location in the PL/SQL code. Use caution with the GOTO command to avoid confusing code. Cursors combine with loops to make useful and powerful programming tools. The next section covers the variations of the loop command.

LOOPS
Looping constructions help you perform repeated tasks with data. There are several variations of LOOP in PL/SQL: 3 CURSOR FOR LOOP. This loop is especially for cursors. The loop starts with the first row fetched and ends with the last row fetched. 3 Simple LOOP. This loop continues infinitely unless you issue an EXIT or EXIT WHEN command. 3 WHILE LOOP. This loop continues as long as some condition is true. When the condition changes to false or null, the looping ends. 3 FOR LOOP. This loop repeats a specified number of times. The next sections show examples of each kind of loop.

Chapter 18 3 Loops

451

CURSOR FOR LOOP


CURSOR FOR LOOP easily handles cursors and repeats a set of code for each row retrieved in the cursor. The loop handles the OPEN, FETCH, and CLOSE of the cursor implicitly. The general syntax of the CURSOR FOR LOOP follows: FOR rowname IN cursorname LOOP loop block END LOOP;

Replace rowname with a valid variable name; you need not declare the rowname beforehand. Replace loop block with any command(s) to use on the fetched row.
Example

The following example inserts a new row into the CHECKUP_HISTORY Table for each animal between six months and one year old.
- -loop_one.sql DECLARE V_ID_NO NUMBER; V_AGE NUMBER; CURSOR ANIMAL IS SELECT ID_NO, TRUNC(SYSDATE) - TRUNC(BIRTH_DATE) AGE_NO FROM AQUATIC_ANIMAL; BEGIN FOR V_ROW IN ANIMAL LOOP IF V_ROW.AGE_NO BETWEEN 180 AND 365 THEN INSERT INTO CHECKUP_HISTORY (CHECKUP_NO, ID_NO, CHECKUP_TYPE) VALUES (CHECKUP_SEQ.NEXTVAL, V_ROW.ID_NO, ANNUAL); END IF; END LOOP; END;

Notice these points in the preceding example: 3 An alias (AGE_NO) is assigned to an expression in the ANIMAL cursor. 3 Column data is prefixed with the variable name V_ROW, which appears in the FOR LOOP command. 3 A sequence (CHECKUP_SEQ.NEXTVAL) assigns a Primary Key value in the INSERT command.
CURSOR FOR LOOP is just the beginning of the looping possibilities. The next section discusses simple LOOP construction.

Simple LOOP
The simple LOOP has no end, technically speaking. The general syntax follows:

452

Chapter 18 3 PL/SQL

LOOP loop block END Loop;

Replace loop block with a series of valid commands. To make it useful, choose one of the following methods of ending the loop: 3 EXIT. Issue an EXIT command after testing for certain conditions. 3 EXIT WHEN condition. Add this condition to the end of your loop block. The loop then tests for the condition each time the loop encounters the statement. Replace condition with a phrase you can evaluate as TRUE or FALSE.
Example

- - loop_two.sql SET SERVEROUTPUT ON DECLARE V_AGE NUMBER; V_COUNT_LOOPS NUMBER := 0; V_TOTAL_AGE NUMBER := 0; CURSOR ANIMAL IS SELECT TRUNC(SYSDATE) - TRUNC(BIRTH_DATE) AGE_NO FROM AQUATIC_ANIMAL; BEGIN OPEN ANIMAL; LOOP V_COUNT_LOOPS := V_COUNT_LOOPS + 1; FETCH ANIMAL INTO V_AGE; V_TOTAL_AGE := NVL(V_AGE,0) + V_TOTAL_AGE; IF V_TOTAL_AGE > 3500 THEN - - end loop if age adds up to more than 3500. DBMS_OUTPUT.PUT_LINE(Total Age: || V_TOTAL_AGE); EXIT; ELSIF V_COUNT_LOOPS > 50 THEN - - end loop if it has run more than 50 times. DBMS_OUTPUT.PUT_LINE(Total Age: || V_TOTAL_AGE); EXIT; END IF; END LOOP; CLOSE ANIMAL; END;

In the preceding example, the predefined PL/SQL Package DBMS_OUTPUT displays the value of a variable on the screen. You can use the DBMS_OUTPUT Package to display error messages and debug.

Chapter 18 3 Loops

453

WHILE LOOP
A WHILE LOOP continues as long as a certain condition is TRUE. The general syntax follows:
WHILE condition LOOP loop block END LOOP;

Replace loop_block with a set of valid PL/SQL statements. Modifying the preceding example, use a WHILE LOOP to test for the maximum age or maximum turns through the loop. The code follows:
- - loop_three.sql SET SERVEROUTPUT ON DECLARE V_AGE NUMBER; V_COUNT_LOOPS NUMBER := 0; V_TOTAL_AGE NUMBER := 0; CURSOR ANIMAL IS SELECT TRUNC(SYSDATE) - TRUNC(BIRTH_DATE) AGE_NO FROM AQUATIC_ANIMAL; BEGIN OPEN ANIMAL; WHILE (V_TOTAL_AGE <= 3500 AND V_COUNT_LOOPS <= 50) Loop V_COUNT_LOOPS := V_COUNT_LOOPS + 1; FETCH ANIMAL INTO V_AGE; V_TOTAL_AGE := NVL(V_AGE,0) + V_TOTAL_AGE; END LOOP; CLOSE ANIMAL; DBMS_OUTPUT.PUT_LINE(Total Age: || V_TOTAL_AGE); END;

The condition in the WHILE LOOP includes two conditions separated by AND.

FOR LOOP
The FOR LOOP enables you to specify the number of times you run through the loop. You can use variables to specify the range of the loop. The general syntax follows:
FOR countvar IN [REVERSE] start_num . . end_num LOOP loop block END LOOP;

Replace countvar with a variable name not declared in the DECLARE clause. This variable is used only within the loop. Replace start_num and end_num with a constant or a numeric variable.

454

Chapter 18 3 PL/SQL

Modifying the preceding example, use a FOR LOOP that executes 50 times. If the age adds up to more than 3500, issue an EXIT command to end the loop early. The code follows:
- - loop_four.sql SET SERVEROUTPUT ON DECLARE V_AGE NUMBER; V_TOTAL_AGE NUMBER := 0; CURSOR ANIMAL IS SELECT TRUNC(SYSDATE) - TRUNC(BIRTH_DATE) AGE_NO FROM AQUATIC_ANIMAL; BEGIN OPEN ANIMAL; FOR V_COUNT_LOOPS IN 1 .. 50 LOOP FETCH ANIMAL INTO V_AGE; V_TOTAL_AGE := NVL(V_AGE,0) + V_TOTAL_AGE; IF V_TOTAL_AGE > 3500 THEN end loop early if total age is more than 3500. EXIT; END IF; END LOOP; CLOSE ANIMAL; DBMS_OUTPUT.PUT_LINE(Total Age: || V_TOTAL_AGE); END;

HTML Extensions
Oracle8 added the Hypertext Markup Language (HTML) extensions to PL/SQL to support Web-enabled applications. To use the HTML extensions, you must install Oracle WebServer, an add-on to your Oracle database Package. You also need to activate SQL*Net. I have found it simpler to use one HTML extension, htm_print(), to handle all the HTML. Learning the other extensions is equivalent to learning HTML and therefore less useful. I recommend you learn HTML instead of relying on the handful of HTML extensions created by Oracle. This approach affords more flexibility in the long run.
CrossReference

To create a Web-enabled application, you must create a Procedure in the database using PL/SQL. See Chapter 19 for more information about Procedures. You can create dynamic Web pages built from your Oracle Tables. The following example displays a simple Web page containing an HTML Table with data from the AQUATIC_ANIMAL Table.
CREATE OR REPLACE PROCEDURE WWW_USER.ANIMAL_WWW

Chapter 18 3 HTML Extensions

455

(I_TANK_NO IN INTEGER default null) IS BEGIN DECLARE V_TANK_NO NUMBER; V_TANK_NAME VARCHAR2(50); /* ================================= Tank Name. */ CURSOR C1 is select T.TANK_NO, T.TANK_NAME from SEAPARK.TANK T where TANK_NO = NVL(I_TANK_NO,T.TANK_NO); /* ================================= Animals for a Tank or for all Tanks. */ CURSOR C2 is select A.ID_NO, A.BIRTH_DATE, A.ANIMAL_NAME, A.DEATH_DATE from SEAPARK.AQUATIC_ANIMAL A, SEAPARK.TANK T where T.TANK_NO = V_TANK_NO and A.TANK_NO = T.TANK_NO ORDER BY A.ID_NO asc; BEGIN htp.htmlOpen; htp.headOpen; htp.title(Seapark - - Animal Inventory List); htp.headClose; htp.print(<BODY BGCOLOR=FFFFFF>); /* ==================================== GET TANK RECORDS */ FOR C1REC in C1 LOOP htp.paragraph; htp.print(<CENTER>); htp.print(<H3><IMG SRC=file:///D|/caroldata/ftp/fish.gif> || Tank: ||C1REC.TANK_NO || : || C1REC.TANK_NAME || </H3>); htp.print(<TABLE BORDER=3>); htp.print(<TR>); htp.print(<TD><B>ID#</B></TD>); htp.print(<TD><B>Name</B></TD>); htp.print(<TD><B>Birth Date</B></TD>); htp.print(<TD><B>Death Date</B></TD>); htp.print(</TR>); V_TANK_NO := C1REC.TANK_NO; FOR C2REC in C2 LOOP /* Loop through one TANKs Animals */ htp.print(<TR>); htp.print(<TD> || C2REC.ID_NO || </TD>); htp.print(<TD> || C2REC.ANIMAL_NAME || </TD>); htp.print(<TD> || C2REC.BIRTH_DATE || </TD>); htp.print(<TD> || C2REC.DEATH_DATE || </TD>);

456

Chapter 18 3 PL/SQL

htp.print(</TR>); END LOOP; htp.print(</TABLE>); htp.print(</CENTER>); END LOOP; htp.line; htp.bodyClose; htp.htmlClose; END; END;

Figure 18-1 shows the resulting Web page.

Handling Errors
You can trap and handle errors using the EXCEPTION clause. The general syntax follows:
EXCEPTION WHEN exception_name THEN exception_block; WHEN exception_name | OTHERS THEN exception_block; END;

Figure 18-1: A dynamic Web page contains the latest values from Oracle Tables.

Chapter 18 3 Handling Errors

457

Replace exception_name with predefined exceptions or User-declared exceptions. Replace exception_block with a set of PL/SQL commands. The OTHERS exception handler traps any error conditions that raise an exception and are not explicitly handled with other exception handlers. OTHERS should always appear at the end of the exception handlers. Table 18-1 lists commonly referenced predefined exceptions and the condition that Triggers them. Many more predefined exceptions exist, but they are seldom required. In addition to the predefined exceptions, you can set up your own exceptions based on any criteria. To define your own exception, follow these steps:

Table 18-1 Predefined Exceptions


Exception name Condition that triggers the exception SQL query retrieves no rows. Implicit query returns more than one row. Duplicate value on unique Index found when attempting to insert or update a row. You have divided by zero. Problem with math function. Value is an invalid number. This exception occurs when inserting or updating a numeric Column with a variable containing data that cannot be converted to a number.

NO_DATA_FOUND TOO_MANY_ROWS DUP_VAL_ON_INDEX ZERO_DIVIDE VALUE_ERROR INVALID_NUMBER

1. Name the exception in the DECLARE section. The general syntax follows:
DECLARE exception_name exception;

2. Raise the exception in the body of the PL/SQL program. The general syntax follows:
RAISE exception_name;

Usually, an exception is raised when certain conditions exist. Test for the conditions with the IF-THEN-ELSE command. 3. Write the exception handler at the end of the PL/SQL block. The general syntax follows:

458

Chapter 18 3 PL/SQL

EXCEPTION exception_name THEN handler_commands; END;

The following quick example program uses a User-defined exception:


- - error_one.sql SET SERVEROUTPUT ON DECLARE AGE_TOO_HIGH exception; V_AGE NUMBER; V_COUNT_LOOPS NUMBER := 0; V_TOTAL_AGE NUMBER := 0; CURSOR ANIMAL IS SELECT TRUNC(SYSDATE) - TRUNC(BIRTH_DATE) AGE_NO FROM AQUATIC_ANIMAL; BEGIN OPEN ANIMAL; LOOP V_COUNT_LOOPS := V_COUNT_LOOPS + 1; FETCH ANIMAL INTO V_AGE; V_TOTAL_AGE := NVL(V_AGE,0) + V_TOTAL_AGE; IF V_TOTAL_AGE > 3500 THEN end loop if age adds up to more than 3500. RAISE AGE_TOO_HIGH; ELSIF V_COUNT_LOOPS > 50 THEN end loop if it has run more than 50 times. DBMS_OUTPUT.PUT_LINE(Total Age: || V_TOTAL_AGE); EXIT; END IF; END LOOP; EXCEPTION WHEN AGE_TOO_HIGH THEN DBMS_OUTPUT.PUT_LINE(Age too high: || V_TOTAL_AGE); WHEN NO_DATA_FOUND THEN DBMS_OUTPUT.PUT_LINE(No rows found); WHEN OTHERS THEN DBMS_OUTPUT.PUT_LINE(Error #|| SQLCODE || Msg: || SQLERRM); END;

This example contains two predefined error handlers: NO_DATA_FOUND and OTHERS.

Chapter 18 3 Summary

459

Summary
PL/SQL is used for Packages, Procedures, Triggers, HTML Web pages (with extensions), and other scripts to handle a wide variety of tasks. This chapter covers the basic construction techniques for using PL/SQL in any context. Cursors give you a procedural way to reference each row of a query that returns multiple rows. Cursors that return a single row can be implicit, rather than named in the DECLARE section. In any case, cursors are either explicitly or implicitly opened, rows are fetched, and the cursors are closed. The IF-THEN-ELSE construction enables you to create logical flows in your program. Use loops to repeat sets of commands a given number of times. Loops are controlled in several different ways: 3 Using a cursor 3 Using a counter 3 Testing a condition The next chapter covers practical applications for PL/SQL in Packages, Procedures, and Triggers. Also, Chapter 25 contains a PL/SQL program using Oracle Objects.

You might also like