IST469-Lab7-AdvancedProgramming

advertisement
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.
2.
3.
4.
solve problems that required a decision or repeated steps,
handle errors in a user friendly manner,
operate separately on a single row returned from a SELECT execution, or
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 Structures
We 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 Control
The 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
Notice there are semi-colons
on the commands after the
THEN and on the END IF;
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.
Here is an example of the IF/THEN command:
DECLARE
DISTANCEVAR
NUMERIC (4) CONSTANT := 500;
BEGIN
IF policyType = ‘HO’ AND
Notice that you can use a
compound condition using
logical operators
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.
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;

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.
IF/THEN/ELSIF format
This decision control structure allows you to test for multiple conditions in the control string.
IF condition1 THEN
Note odd
spelling
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.
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
You can have as many of these ELSIF
commands as practical.
(‘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
You can have as many of
these WHEN/THEN blocks as
practical.
('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;
Hands-On Examples
Vote 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 Control
Sometimes 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;

Notice that you need to end
the loop with an END LOOP;
command.
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:
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
The counter_variable does
not have to be defined in
the DECLARE section.
Start and end values must
be integers
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;
The vLoopCount
counter variable can
not be referenced
outside of the FOR
LOOP unless you
explicitly declare it in
the DECLARE section.
BEGIN
FOR vLoopCount IN 1 .. 5
LOOP
No semi-colon at the
end of the FOR
deptTotalSalaryVar := deptTotalSalaryVar + &empSalarySV;
END LOOP;
DBMS_OUTPUT.PUT_LINE ('Dept Salary:
END;
' || deptTotalSalaryVar);
Hands-On Examples
Multiples 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
Execution Statements
Exception Statements
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 :
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 Exceptions
There 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 exception
2. Associate the exception with the Oracle Error Number
DECLARE
Declare an exception variable
exceptional_ex
EXCEPTION;
using the datatype EXCEPTION
PRAGMA EXCEPTION_INIT (exceptional_ex, -#####);
BEGIN
Code goes here;
Associate that exception with a specific error
Oracle error number (do not forget the - )
EXCEPTION
WHEN exceptional_ex THEN
do something here;
END;
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:
ORA-02291: integrity constraint (FUDGEMART.STATE_FK) violated - parent key not found
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:
AUTHOR:
DATE:
PURPOSE:
Customer Insert
Susan Dischiave
2/28/2006
To insert a record into the customer table
*/
DECLARE
cIDVar
cNameVar
cStateVar
CUSTOMER_T.Customer_ID%TYPE
:=
CUSTOMER_T.Customer_Name%TYPE :=
CUSTOMER_T.Customer_State%TYPE :=
&cIDSV;
&cNameSV;
&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);
Process the NoStateFoundEx when Oracle BEGIN
02291 occurs
--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
There is no entry for state xx
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 Exceptions
Oracle has provided a simple method for users to implement their own custom, user-defined, exceptions. The steps
involved are to:
1.
2.
3.
4.
5.
declare an exception variable using the EXCEPTION datatype,
check for the error condition using a decision control structure,
raise an exception condition using the RAISE command,
transfer control to the exception handling section,
and handle the exception.
The format is:
DECLARE
MY_ERROR_EX
EXCEPTION;
Declare an exception variable using
the EXCEPTION datatype
BEGIN
IF some condition
Check for the error condition
THEN
RAISE MY_ERROR_EX
Raise the error (transfer control to the
exception handler)
END IF;
Some other program commands;
EXCEPTION
WHEN MY_ERROR_EX
THEN
END;
some program commands;
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
MIN_WAGE_CON CONSTANT
empSalaryVar
INVALID_SALARY_EX
NUMERIC (7)
NUMERIC (7)
NUMERIC(7)
EXCEPTION;
:=
:=
:=
--
0;
6;
&empSalarySV;
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. Cursors
Now 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.
2.
3.
4.
declare the cursor in the DECLARE section,
in the code section (BEGIN) open the cursor,
fetch a row and continue until there are no more rows left in the cursor,
finally, close the cursor.
DECLARE
CURSOR testCur
IS
SELECT * FROM TestTable;
testTableRec
TestTable%ROWTYPE;
BEGIN
OPEN testCur;
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
LOOP
FETCH testCur INTO testTableRec;
EXIT WHEN testCur%NOTFOUND;
EXIT when there are no more rows
execute some commands;
END LOOP;
CLOSE testCUR;
Close the cursor when processing is
completed
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
Over $500
Over $50
50 or under
Discount
25%
15%
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.
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 recalculate 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.
Newly added columns
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 :
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:
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.
4. Stored Procedures
This 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!
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
Procedure Name
(
variable
IN/OUT
dataType
defaultValue
)
IS
Define all IN/OUT
parameters
Declare all variables here
BEGIN
Place PL/SQL here
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 :
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:
That’s our stored
procedure here.
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.
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 Own
Use 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.
Download