32
Lab 6 – Advanced PL/SQL Programming SOLVING MORE COMPLEX PROBLEMS In the last lecture you were introduced to PL/SQL and the unnamed or anonymous procedure blocks. These procedure blocks provide the database developer with the capability to build executable program units that can take some action on the actual data stored in your database. However, we were limited in what we could do. We were unable to for example: 1. solve problems that required a decision or repeated steps, 2. handle errors in a user friendly manner, 3. operate separately on a single row returned from a SELECT execution, or 4. execute a procedure with out entering the syntax into the PL/SQL editor. This time we will explore techniques to solve these problems. By creating reusable modules and by implementing additional control structures, we will create program units that are more robust and flexible. This will allow us to better solve business problems. First, the only procedural construct that we have explored is Sequential Structures. So far, all we do is simply processed one instruction after another. This construct can only be used to solve simple problems. In order to solve more complex problems, however, we need the ability to make decisions as well as repeat steps. To do this we will use Decision Control Structures to make decisions and the Iterative Control Structures to repeat steps. Second, in order to handle errors in a more flexible and “user friendly” manner, we will implement Exception Handling. In addition to displaying more descriptive messages, the database designer can provide error handling routines. Programs can provide a way for the user to correct the problem instead of simply exiting the program. Third, we will discover how to manipulate a single row of data contained in a data set. When executing a SELECT statement in PL/SQL, Oracle establishes a work area that contains the set of data. This work area containing the set of data is

Lab 6 – Advanced PL/SQL Programming - …classes.ischool.syr.edu/ist469/Content/wk08/IST469-Lab7... · Web viewConnect to SQL Developer as IST469 and open a new sql file. Type the

Embed Size (px)

Citation preview

Lab 6 – Advanced PL/SQL Programming

SOLVING MORE COMPLEX PROBLEMSIn the last lecture you were introduced to PL/SQL and the unnamed or anonymous procedure blocks. These procedure blocks provide the database developer with the capability to build executable program units that can take some action on the actual data stored in your database. However, we were limited in what we could do. We were unable to for example:

1. solve problems that required a decision or repeated steps,2. handle errors in a user friendly manner,3. operate separately on a single row returned from a SELECT execution, or4. execute a procedure with out entering the syntax into the PL/SQL editor.

This time we will explore techniques to solve these problems. By creating reusable modules and by implementing additional control structures, we will create program units that are more robust and flexible. This will allow us to better solve business problems.

First, the only procedural construct that we have explored is Sequential Structures. So far, all we do is simply processed one instruction after another. This construct can only be used to solve simple problems. In order to solve more complex problems, however, we need the ability to make decisions as well as repeat steps. To do this we will use Decision Control Structures to make decisions and the Iterative Control Structures to repeat steps.

Second, in order to handle errors in a more flexible and “user friendly” manner, we will implement Exception Handling. In addition to displaying more descriptive messages, the database designer can provide error handling routines. Programs can provide a way for the user to correct the problem instead of simply exiting the program.

Third, we will discover how to manipulate a single row of data contained in a data set. When executing a SELECT statement in PL/SQL, Oracle establishes a work area that contains the set of data. This work area containing the set of data is referred to as a cursor. A cursor can be thought of as a pointer to a table in the database buffer cache. By declaring a cursor, we can process the returned rows individually. Therefore, using this technique, we can examine and manipulate one row at a time.

Finally, the only was we could execute the anonymous procedural program unit was to enter the instructions (PL/SQL commands) directly into one of the PL/SQL editors or reference a file from within SQL*Plus. To keep the procedure, we stored the commands in a text (.txt) or SQL (.sql) file outside the database. This method, however, is not very practical and prone to error. We will discover how to store the program units in the database by creating named stored procedures.

Learner Outcomes

Management of Solution Development By completely this lab, you will achieve a deep level of knowledge and comprehension of the disciplines used in the development of information system solutions. You will develop the ability to apply these disciplines to the solution of organizational and business problems. Specifically, after completing this lab, you will be able to:

1. Design, construct, and maintain a database and various database objects using procedural language constructs.2. Design and implement a complete problem solution using current database technology (Oracle 11g Database)

To accomplish this you will:

Implement decision control and iterative control structures Create and exception handling routines Implement record level processing in Oracle 11g using cursors Create and implement callable (stored) procedures in Oracle 11g

So, let’s get started!!!

1. Control StructuresWe will extend our knowledge of procedures by using some of the more popular procedural constructs known as decision control structures and iterative control structures. These two families of constructs allow you to change the control flow of a program unit from purely sequential to one where you are in command of how the logic within the procedure flows.

Decision ControlThe PL/SQL commands used to make decisions, that is, alter the order in with commands are executed are the IF/THEN, IF/THEN/ELSE and the IF/THEN/ELSIF.

IF/THEN format

IF condition THEN

Commands that you want to execute if the condition is true;

END IF;

The IF portion of the command tests the condition. If the condition is true, control is transferred to the command that immediately follows the THEN. If the condition is false, control is transferred to the command following the END IF.

Notice there are semi-colons on the commands after the THEN and on the END IF;

Here is an example of the IF/THEN command:

DECLARE

DISTANCEVAR NUMERIC (4) CONSTANT := 500;

BEGIN

IF policyType = ‘HO’ AND

fireHydrantLoc > DISTANCEVAR THEN

DBMS_OUTPUT.PUT_LINE(‘Insured property must be within ’||

DISTANCEVAR ||’feet’);

END IF;

END;

IF/THEN/ELSE format

IF condition THEN

Commands that you want to execute if the condition is true;

ELSE

Commands that you want to execute if the condition is false;

END IF;

The IF part of the command tests the condition. If the condition is true, control is transferred to the command that immediately following the THEN. If the condition is false, control is transferred to the command following the ELSE.

Notice that you can use a compound condition using logical operators

Here is an example of the IF/THEN/ELSE command:

DECLARE

DISTANCEVAR Numeric (4) CONSTANT := 500;

BEGIN

IF policyType = ‘HO’ AND

fireHydrantLoc > DISTANCEVAR

THEN

DBMS_OUTPUT.PUT_LINE(‘Insured property must be within ’|| DISTANCEVAR || ‘ feet of a fire hydrant’);

ELSE

DBMS_OUTPUT.PUT_LINE(‘Insured property is within the acceptable distance to a fire hydrant);

END IF;

END;

IF/THEN/ELSIF format

This decision control structure allows you to test for multiple conditions in the control string.

IF condition1 THEN

Commands that you want to execute if the condition1 is true;

ELSIF condition2 THEN

Commands that you want to execute if the new condition2 is true;

ELSE

Commands that you want to execute if the new condition2 is false;

END IF;

The IF portion of the command tests condition1. If condition1 is true, control is transferred to the command that immediately following the THEN. If the condition1 is false, control is transferred to the ELSIF command where condition2 is tested. If condition2 is true control is transferred to the command following the second THEN. If condition2 is false control is transferred to the command following the ELSE.

Notice that when the condition is false the ELSE path is taken. You can also nest your IF statements by including them after the ELSE.

Note odd spelling

Here is an example of the IF/THEN/ELSIF command:

DECLARE

DISTANCEVAR Numeric (4) CONSTANT := 500;

BEGIN

IF policyType = ‘HO’ AND

fireHydrantLoc > DISTANCEVAR THEN

DBMS_OUTPUT.PUT_LINE(‘Insured property must be within ’|| DISTANCEVAR ||‘ feet of a fire hydrant’);

ELSIF policyType = ‘FO’ THEN

DBMS_OUTPUT.PUT_LINE

(‘Insured property does not require access to a fire hydrant);

ELSE

DBMS_OUTPUT.PUT_LINE

(‘Insured property has required access to a fire hydrant);

END IF;

END;

An alternative to the IF/THEN/ELSIF command would be to use the CASE structure which for the previous example would look like this:

DECLARE

DISTANCEVAR NUMERIC (4) CONSTANT:= 500;

BEGIN

CASE

WHEN policyType = 'HO' AND fireHydrantLoc > DISTANCEVAR

THEN DBMS_OUTPUT.PUT_LINE ('Property must be within '|| DISTANCEVAR || ' feet of a fire hydrant');

WHEN policyType = 'FO'

THEN DBMS_OUTPUT.PUT_LINE

('Insured property does not require access to a fire hydrant');

ELSE DBMS_OUTPUT.PUT_LINE

('Insured property has required access to a fire hydrant');

END CASE;

END;

You can have as many of these ELSIF commands as practical.

You can have as many of these WHEN/THEN blocks as practical.

Hands-On ExamplesVote for Pedro. Let’s write a program which asks for the number of votes for Pedro. When Pedro has more than 200 votes, he wins!

Do This: Connect to SQL Developer as IST469 and open a new sql file. Type the following code into the query window as save it as vote-for-pedro.sql :

Run this program a couple of times entering different values each time. See if you can make Pedro win and lose the election with different values for vote_countvote

A better Vote for Pedro. Next, let’s write a program which is a little more descriptive with the narrative when Pedro wins or loses.

Do This: Connect to SQL Developer as IST469 and open a new sql file. Type the following code into the query window as save it as a-better-vote-for-pedro.sql :

Iterative ControlSometimes you need to execute a certain logic pattern or a series of commands many times in a row before doing something else. We call this process of managing iterative control, looping. There are two types of looping: pretest and posttest.

You would use pretest looping when you want to evaluate an “exit condition” (that is determining when to stop the looping process) before your commands are executed.

You would use posttest looping when you need to execute the commands in your loop at least once. There are five different looping control structures: LOOP/EXIT, LOOP/EXIT/WHEN, WHILE/LOOP, FOR/LOOP and CURSOR/FOR/LOOPS. For this lab we will discuss the first four. Cursor/For/Loops will be discussed later in the semester.

LOOP/EXIT format. This can be either a pretest or a posttest loop depending on how you structure your statements.

LOOP

Commands you want to execute

IF exit condition THEN

EXIT;

END IF;

END LOOP;

The LOOP keyword signals the beginning of the loop process followed any commands you want executed. An IF/THEN control structure tests the loop’s exit condition. If the exit condition is true, control is transferred to the EXIT which signals the end of the looping process.

Here is an example of the LOOP/EXIT command:

DECLARE

deptTotalSalaryVar Numeric (7) := 0;

empSalaryVar Numeric (7) := 1000;

BEGIN

LOOP

deptTotalSalaryVar := deptTotalSalaryVar + empSalaryVar;

IF deptTotalSalaryVar > 1000000 THEN

EXIT;

END IF;

END LOOP;

END;

LOOP/EXIT/WHEN format. This can be either a pretest or a posttest loop depending on how you structure your statements.

LOOP

Commands you want to execute

EXIT WHEN condition;

END LOOP;

The LOOP keyword signals the beginning of the loop process followed any commands you want executed. WHEN control structure tests the loop’s exit condition. If the exit condition is true, control is transferred to the EXIT which signals the end of the looping process.

Here is an example of the LOOP/EXIT/WHEN command:

Notice that you need to end the loop with an END LOOP; command.

DECLARE

deptTotalSalaryVar Numeric (7) := 0;

empSalaryVar Numeric (7) := 1000;

BEGIN

LOOP

deptTotalSalaryVar := deptTotalSalaryVar + empSalaryVar ;

EXIT WHEN deptTotalSalaryVar >1000000;

END LOOP;

DBMS_OUTPUT.PUT_LINE ('Dept Salary: ' || deptTotalSalaryVar);

END;

WHILE/LOOP format. This is a pretest loop that evaluates the exit condition before any commands are executed.

WHILE exit condition

LOOP

Commands you want to execute

END LOOP;

The WHILE evaluates the exit condition before the LOOP keyword signals the beginning of the loop process. If the exit condition is true control is transferred to the commands following the LOOP keyword. If the exit condition is false control is transferred to the END LOOP; which signals an exit from the looping process.

Here is an example of the WHILE/LOOP command:

DECLARE

vDeptTotalSalary Numeric (7) := 0;

vEmpSalary Numeric (7) := 1000;

BEGIN

WHILE vDeptTotalSalary <1000000

LOOP

vDeptTotalSalary := vDeptTotalSalary + vEmpSalary;

END LOOP;

DBMS_OUTPUT.PUT_LINE ('Dept Salary: ' || vDeptTotalSalary);

END;

FOR LOOP format. This is a posttest loop that evaluates the exit condition after the commands are executed.

FOR counter_variable IN start_value .. end_value

LOOP

Commands you want to execute

END LOOP;

The FOR evaluates the exit condition before the LOOP keyword signals the beginning of the loop process. Control is transferred to the commands following the LOOP keyword. The loop increments the variable counter by one until it equals the end value. When the end value is reached, i.e. the exit condition, false control is transferred to the END LOOP; which signals an exit from the looping process.

Here is an example of the FOR LOOP command:

DECLARE

deptTotalSalaryVar Numeric (7) := 0;

BEGIN

FOR vLoopCount IN 1 .. 5

LOOP

deptTotalSalaryVar := deptTotalSalaryVar + &empSalarySV;

END LOOP;

DBMS_OUTPUT.PUT_LINE ('Dept Salary: ' || deptTotalSalaryVar);

END;

The counter_variable does not have to be defined in the DECLARE section.

Start and end values must be integers

The vLoopCount counter variable can not be referenced outside of the FOR LOOP unless you explicitly declare it in the DECLARE section.

No semi-colon at the end of the FOR

Hands-On ExamplesMultiples of. Let’s write a program that uses a loop. This program will ask you for two numbers and generate multiples.Do This: Connect to SQL Developer as IST469 and open a new sql file. Type the following code into the query window as save it as multiples-of.sql :

Run this code a few times to better understand what it is doing. It should be fairly straightforward.

2. Exception Handling

So, now we can really solve complex business problems. We can execute one instruction after another (Sequential Control), make decisions (Decision Control), and even repeat steps (Iterative Control) if necessary. This is great as long as everything is perfect. That is, when executing a select query for a specific record with a Where clause, the record exists and any data requested from the user is entered in a valid format. These are only a few examples of potential errors. There exist various potential problems that could cause program units to not execute properly. For example, what would happen in the previous example if the data in the multiple field was entered as an alphabetic such as ‘abc’ and not a valid numeric value? It just so happens that Oracle will display an error message similar to the one below and terminate the execution of the code.

There is no way to test for every possible combination of data that might be entered, the Oracle error message is not user friendly, and there is no way to recover. To solve this problem, Oracle has implemented a technique to except and handle errors (situations that should not occur) using an architecture referred to as exception handling.

The format of the exception handling architecture is:

BEGIN

EXCEPTION

END;

Control transfers to the EXCEPTION section when a defined exception occurs.

So, now let’s look at the previous example with an exception section added to trap a VALUE error.

Do This: Connect to SQL Developer as IST469 and open a new sql file. Type the following code into the query window as save it as multiples-of-with-exception-handling.sql :

Execution Statements

Exception Statements

Try to execute this code and at one of the prompts, enter something which is not a number, for example:

And you should see the following error message at execution:

This is a much nicer way to handle execution errors and there are a variety of predefined exceptions available to use such as NO_DATA_FOUND, TOO_MANY_ROWS, and ZERO_DIVIDE. You can find a table of predefined exception in Oracle’s “PL/SQL User’s Guide and Reference”. A list of common predefined exceptions can also be located in the “Guide to Oracle 91” text.

Undefined ExceptionsThere are errors that are not as common that could occur and they have not be assigned a name. There are exceptions that are generated by Oracle but they do not have “user friendly” name that can easily be tested for. When these errors occur, the error message displayed may make sense to someone who knows the database; but, the message would not make sense to an end user. You can handle these exception by defining them in the DECLARE section of your procedure. To do this you would:

1. Declare an exception2. Associate the exception with the Oracle Error Number

DECLARE

exceptional_ex EXCEPTION;

PRAGMA EXCEPTION_INIT (exceptional_ex, -#####);

BEGIN

Code goes here;

EXCEPTION

WHEN exceptional_ex THEN

do something here;

END;

Declare an exception variable

using the datatype EXCEPTION

Associate that exception with a specific error

Oracle error number (do not forget the - )

Do this when the exception occurs

Let’s look at an example. Suppose, we had a customer table that contained an attribute for the State two character abbreviation. We also have a state table and the State abbreviation code is a foreign key that references the State table. Now, suppose we have an unnamed stored procedure that inserts values into the table based on substitution variables. If we enter data and use a state abbreviation that does not have an entry in the State table, we would get a foreign key constraint violation. This is an Oracle – 02291 and the error message that would be displayed would look something like the following:

To avoid this problem we could create our own exception and let the complier know which Oracle error number to associate with this newly defined exception using the above format. The following PL/SQL unnamed procedure creates a NoStateFoundEX and will now throw an error that has a more meaningful message;

/* PROGRAM: Customer Insert AUTHOR: Susan Dischiave DATE: 2/28/2006 PURPOSE: To insert a record into the customer table*/

DECLARE cIDVar CUSTOMER_T.Customer_ID%TYPE := &cIDSV; cNameVar CUSTOMER_T.Customer_Name%TYPE := &cNameSV; cStateVar CUSTOMER_T.Customer_State%TYPE := &cStateSV; -- Declare an exception for No State Found NoStateFoundEx EXCEPTION; -- Associate the Oracle exception with the NOStateFound -- use the Oracle error code for foreign key constraint error PRAGMA EXCEPTION_INIT (NoStateFoundEx, -02291);

BEGIN -- -- Insert values into the customer table -- using the substitution variables -- INSERT INTO Customer_T (Customer_ID, Customer_Name, Customer_State) VALUES (cIDVar, cNameVar, cStateVar);EXCEPTION WHEN NoStateFoundEx THEN

DBMS_OUTPUT.PUT_LINE('There is no entry for state ' || cStateVar);END;

Now the error message that is displayed is

ORA-02291: integrity constraint (FUDGEMART.STATE_FK) violated - parent key not found

There is no entry for state xx

Process the NoStateFoundEx when Oracle -02291 occurs

The Oracle developers could not possibly have thought of all possible exceptions that you might want to capture. For example, a grade entry procedure for Syracuse University might want to raise an exception if a grade value of Z was entered. An employer would probably not want an employee’s birth date to be greater than the current date. There are numerous custom exceptions that would cause inaccurate data in the database if they were not caught and handled properly. You can avoid this problem that could potentially result in anomalies by creating your own exceptions.

User-Defined ExceptionsOracle has provided a simple method for users to implement their own custom, user-defined, exceptions. The steps involved are to:

1. declare an exception variable using the EXCEPTION datatype,2. check for the error condition using a decision control structure,3. raise an exception condition using the RAISE command, 4. transfer control to the exception handling section,5. and handle the exception.

The format is:

DECLARE

MY_ERROR_EX EXCEPTION;

BEGIN

IF some condition

THEN

RAISE MY_ERROR_EX

END IF;

Some other program commands;

EXCEPTION

WHEN MY_ERROR_EX

THEN some program commands;

END;

Declare an exception variable using the EXCEPTION datatype

Check for the error condition

Raise the error (transfer control to the exception handler)

Handle the error in an appropriate manner

Suppose in the previous example, we wanted to check the salary to make sure that the hourly wage was at or above the NYS minimum wage. The procedure might look like this:

DECLARE deptTotalSalaryVar NUMERIC (7) := 0; MIN_WAGE_CON CONSTANT NUMERIC (7) := 6; empSalaryVar NUMERIC(7) := &empSalarySV; INVALID_SALARY_EX EXCEPTION; -- declare the exception variable

BEGIN-- check that the salary is above minimum wage IF empSalaryVar < MIN_WAGE_CON THEN RAISE INVALID_SALARY_EX; END IF;

FOR vLoopCount IN 1 .. 5 LOOP deptTotalSalaryVar := deptTotalSalaryVar + (40*empSalaryVar); END LOOP;

DBMS_OUTPUT.PUT_LINE ('Dept Salary: ' || TO_CHAR(deptTotalSalaryVar)); EXCEPTION WHEN VALUE_ERROR THEN

DBMS_OUTPUT.PUT_LINE('An invalid value was entered for the employee salary');

WHEN INVALID_SALARY_EX THEN DBMS_OUTPUT.PUT_LINE('The salary IS BELOW minimum wage ');

END;

3. CursorsNow that we can write program units (procedures) that can solve complex problems, we need to access the data. The data required for the procedures is stored in the database tables. And, we already know the mechanism supplied by SQL that allows us to retrieve the desired rows. You are already familiar with the SQL SELECT command. This command allows you to specify the table, attributes, and even specify criteria for rows that you wish to retrieve. Upon execution, the data values are returned into a work area (database buffer cache). In addition, Oracle creates a pointer to the area called a cursor.

There are implicit and explicit cursors. An implicit cursor is automatically established by Oracle every time an SQL statement is executed. The user is unaware of this cursor and has no way to control the process or the data using this cursor. If, however, you would like to have control and access each row individually, you can create an explicit cursor. Think of an explicit cursor as a pointer to the database buffer cache that has a name you know. Since you know the name that contains the address, you can now use the name to go to the address.

So, let’s take a closer look at how to use an explicit cursor. The process includes:

1. declare the cursor in the DECLARE section,2. in the code section (BEGIN) open the cursor,3. fetch a row and continue until there are no more rows left in the cursor, 4. finally, close the cursor.

DECLARE

CURSOR testCur ISSELECT * FROM TestTable;

testTableRec TestTable%ROWTYPE;

BEGIN OPEN testCur;

LOOPFETCH testCur INTO testTableRec;

EXIT WHEN testCur%NOTFOUND;

execute some commands;

END LOOP; CLOSE testCUR;END;

Let’s take a look at a specific example. The FUDGEMART schema contains a PRODUCTS table. Suppose we want discount all products in the ‘Electronics’ department based on the following discount rule structure.

Retail Price DiscountOver $500 25%Over $50 15%50 or under 5%

It would be difficult, if not impossible to write a SELECT query to do this – sometimes the only way to solve a problem like this is to use a cursor to cycle and process individual rows.

Declare a cursors which includes the SELECT required to retrieve the data

Declare a variable to hold the individual row Fetched

Open the cursor

Fetch (read) a single row from the cursor

EXIT when there are no more rows

Close the cursor when processing is completed

Do This: Connect to SQL Developer as IST469 and open a new sql file. Type the following code into the query window as save it as fudgemart-eletronics-discounts.sql :

When you execute the anonymous procedure, you should see the following output:

A more useful means of writing this cursor would be to store the discounts somewhere so that we do not have to re-calculate them each time someone considers placing an order. We’ll alter the PRODUCTS table to accommodate discounts and then re-write the procedure.

Do This: First let’s alter the FUDGEMART.PRODUCTS table and add some columns to handle discounts.

Make sure your new columns are there and doing their duties:

Now that things are going to plan, we need to we-write our cursor-using anonymous procedure to apply discounts to the table (rather that writing them to the screen).

Do This: Connect to SQL Developer as IST469 and open a new sql file. Type the following code into the query window as save it as process-fudgemart-eletronics-discounts.sql :

Newly added columns

After you get this script to execute without error, it might not seem to do anything but if your check the products table, you should see your new price structure!

Remember that because we changed data, we‘re going to need to use COMMIT to save those changes, or ROLLBACK when we don’t want to keep them. This is very useful when debugging your procedures as you can always rollback any pending changes to your data. If you don’t commit this update it will not persist through your database session, so it is important to COMMIT or ROLLBACK ASAP. It’s never a good idea to leave open transactions kicking around so it’s best to write this procedure to be transaction safe by including logic to commit or rollback inside the procedure itself:

4. Stored ProceduresThis is all really great. We have the ability to solve complex problems, handle errors effectively, and process a single row at a time. But, we’re still entering the commands in an editor separate from the database and storing the anonymous procedures in a separate location. We also have no effective way to share our program units with other users. By creating stored procedures, however, we can store the program unit in a convenient location, the database, and allow access to any other user that we want. In addition, stored procedures allow for input parameters as well as output parameters. That is, you can pass a value to the procedure to use when it executes and/or you can return a value to the calling procedure. We could have certainly used this in our last example!

At the end of the procedure, I commit.

If at any point, the you-know-what hits the you-know-where I rollback and pass the exception along.

So, let’s take a look at the basic format required to create or replace a stored procedure.

The format of a stored procedure is:

CREATE OR REPLACE PROCEDURE MyProcedureName

(

variable IN/OUT dataType defaultValue

)

IS

BEGIN

END;

The command to execute a stored procedure from SQL*Plus or other editor:

EXEC MyProcedureName (parameter list);

We can now take one of the previous procedures (FOR LOOP demo) and create a stored procedure. Instead of using substitution variables, we can pass the input as parameters.

Do This: Connect to SQL Developer as IST469 and open a new sql file. Type the following code into the query window as save it as create-listmultiples.sql :

Procedure Name

Define all IN/OUT parameters

Declare all variables here

Place PL/SQL here

One thing you’ll notice about a stored procedure is that when you execute the code to define the procedure it does not actually execute the procedure logic. It simply compiles the procedure and stores it in the IST469 schema. Observe from SQL Developer:

Now that we’ve stored our procedure, let’s execute it.

Do This: Connect to SQL Developer as IST469 and open a new sql file. Type the following code into the query window as save it as run-listmultiples.sql :

I hope you can see the obvious advantages of using stored procedures. The convenience of storing the PL/SQL in the catalog allows gives you that “write-once, run many” advantage. Also since the procedure itself is abstracted and encapsulated you can secure it using SQL like you can with tables and views.

Not only can these procedures be easily accessed by other database users, but They can also be accessed by other procedures. Stored procedures can execute other stored procedures passing and returning parameters. This technique allows us to create small reusable modules which ultimately facilitate quicker development and easier maintenance.

In this final example, let’s write a transaction safe procedure to add a new Fudgemart vendor, and then demonstrate how to execute the procedure.

That’s our stored procedure here.

Do This: Connect to SQL Developer as IST469 and open a new sql file. Type the following code into the query window as save it as create-addvendor.sql :

Next, execute the procedure adding this new vendor:

And you should see this output:

Now we can make the database more modular allowing us to take advantage of reuse and reliability. Creating stored procedures allow us to create small reusable program units. We also now can process individual rows of data (not just sets of records) using cursors; and, we can also make our units of code display messages that have meaning for the users!

Lab Assignment – On Your OwnUse the FUDGEMART schema to complete each of the following problems. For each question you must provide a screenshot of your code and a screen shot which proves your code executed properly.

1. Write an anonymous procedure which, given an order ID will calculate and display the total amount of the order. HINT: the total amount should be the sum of the order quantity * the product retail price. Execute your stored procedure to verify it works. As a self-check, Order 19’s total should be $24.49, order 693 is $222.39 and order 118 should be $19.95

2. Alter your fudgemart.orders table twice. The first time add a new column called order_tax of type decimal(10,2) which does not allow nulls and uses a default value of zero. The second time add a new column called order_total of type decimal(10,2) which does not allow nulls and uses a default value of zero.

3. Write stored procedure called TotalOrders which will calculate the total of each order (like in question 1) and then store the order amount in the order_total column of the fudgemart.orders table. HINT: Use a cursor to solve this problem similar to what we did in the lab.

4. Since Fudgemart now has a warehouse in California, orders shipped in CA must pay 11% sales tax. Write an anonymous stored procedure which updates the order_tax with the column for any orders for customers in CA. HINT: As a self-check, order 693 (a CA order) should have a sales tax of 24.4629.

5. Fudgemart just opened an East-coast warehouse in New York, and as a result must now collect sales tax at a rate of 8% on orders shipped to NY, in addition to the 11% sales tax for CA shipped orders. Since this might happen for another state in the future, write a named stored procedure called LevySalesTax which, when given a two character state code such as ‘NY’ and a tax rate such as 0.08 will update all orders to customers in that state to include the appropriately calculated sales tax for that order. Make the procedure transaction-safe execute the procedure to update sales tax for NY.