/*===
328lect05-2.sql
* creates and tests REFACTORED version of 328lect05-1.sql's
PL/SQL stored procedure job_overview, having it call
328lect05-1's PL/SQL stored function job_count
* creates and tests PL/SQL stored procedure loopy,
an overwrought beast that demos three different kinds
of PL/SQL loops and some exception-handling
* creates and tests PL/SQL stored procedure reverse_play,
that prints the results of trying out several classic
for loops, two with reverse and two without
by: Sharon Tuttle
last modified: 2024-02-15
===*/
/*===
procedure: job_overview: varchar2 -> void
purpose: expects a job title, has the side-effect
(if serveroutput is on) of printing to the screen
a report including that job title, how many employees
have that job title, and their average salary
uses: function job_count (from W5-1 examples)
examples:
if set-up-ex-tbls.sql has been run, and serveroutput is on,:
exec job_overview('President')
...should print to the screen:
Job Title : President
# with Job: 1
Avg Salary: $5000
exec job_overview('Sales')
...should print to the screen:
Job Title : Sales
# with Job: 4
Avg Salary: $1400
exec job_overview('Kitten Wrangler')
...should print to the screen:
Job Title : Kitten Wrangler
# with Job: 0
Avg Salary: $0
===*/
create or replace procedure job_overview(job_ttl varchar2) is
num_w_job integer;
job_avg_sal number(7, 2);
begin
-- how many have this job title?
num_w_job := job_count(job_ttl);
-- what is their salary?
if (num_w_job > 0) then
select avg(salary)
into job_avg_sal
from empl
where job_title = job_ttl;
else
job_avg_sal := 0;
end if;
dbms_output.put_line('Job Title : ' || job_ttl);
dbms_output.put_line('# with Job: ' || num_w_job);
dbms_output.put_line('Avg Salary: $' || job_avg_sal);
end;
/
show errors
set serveroutput on
/*=== TESTING job_overview ===*/
prompt ============
prompt TESTING job_overview
prompt
prompt ============
prompt testing job_overview('President'):
prompt should see output including President, 1, and $5000:
prompt ============
exec job_overview('President')
prompt ============
prompt testing job_overview('Sales'):
prompt should see output including Sales, 4, and $1400:
prompt ============
exec job_overview('Sales')
prompt ============
prompt testing job_overview('Kitten Wrangler'):
prompt should see output including Kitten Wranger, 0, and $0:
prompt ============
exec job_overview('Kitten Wrangler')
/*=====
procedure: loopy: integer -> void
purpose: expects a desired quantity of burps,
and has various odd side effects,
such as:
* printing the given quantity of BURPs
to the screen,
* printing a GLIP message with 13-16 after it,
* printing the names and locations of all current
departments,
* printing the names of any employees whose
salary is 1000 times the given burp quantity,
* if no exception has been raised before this,
printing a message giving an auto-declared i
variable's value in a loop
=====*/
create or replace procedure loopy(burp_quant integer) as
counter integer;
chosen_empl empl.empl_last_name%type;
begin
counter := 0;
-- using a WHILE loop to print BURP! a number
-- of times based on the burp_quant parameter
while (counter < burp_quant) loop
dbms_output.put_line('BURP! ' || counter);
counter := counter + 1;
end loop;
-- demonstrating a "classic" FOR loop
dbms_output.put(chr(10));
for counter in 13 .. 16 loop
dbms_output.put_line('GLIP! ' || counter);
-- uncomment the statement below to see you CANNOT
-- change the loop control variable in a for loop
-- within the loop:
--
-- counter := 20;
end loop;
-- demonstrating a cursor-controlled FOR loop
dbms_output.put(chr(10));
for next_dept in (select *
from dept
order by dept_name) loop
dbms_output.put_line(next_dept.dept_name || ' is in '
|| next_dept.dept_loc);
end loop;
-- to demo exception handling, try to get the name
-- of an empl whose salary is 1000 times the
-- burp_quant parameter
select empl_last_name
into chosen_empl
from empl
where salary = burp_quant * 1000;
dbms_output.put(chr(10));
dbms_output.put_line('Look! ' || chosen_empl || ' has salary '
|| ' that is 1000 times ' || burp_quant);
/*=== AFTER-CLASS EXPERIMENT
* I wanted to double-check that the loop-control
variable for a "classic" for-loop would be
automatically declared if not already declared
as a local variable (as we saw above for a
cursor-controlled for loop)
* loop below verifies this
* then I wondered: is PL/SQL like C++, in which
a variable declared (or, for PL/SQL, auto-declared)
in a for-loop's first line has as its scope JUST
that for-loop's body?
COMMENTED-OUT put_line below verifies that this
IS also the case in PL/SQL -- uncomment it to
see the I-is-undeclared error message!
====*/
dbms_output.put(chr(10));
for i in 100 .. 103 loop
dbms_output.put_line('i has value: ' || i);
end loop;
-- uncomment to see that auto-declared i's scope is
-- JUST the for loop's body
--
--dbms_output.put_line('is i in scope after loop? ' || i);
exception
-- no_data_found raised when try to project an attribute
-- value into a local variable but no rows are selected
when no_data_found then
dbms_output.put(chr(10));
dbms_output.put_line('No employee has salary ' ||
(burp_quant * 1000));
-- too_many_rows raised when try to project an attribute
-- value into a single-valued-type local variable
-- but more than one row is selected
when too_many_rows then
dbms_output.put(chr(10));
-- my choice for this exception: print the message for
-- each such employee
for next_empl in (select empl_last_name
from empl
where salary = burp_quant * 1000)
loop
dbms_output.put_line('Look! ' || next_empl.empl_last_name
|| ' has salary that is 1000 times ' || burp_quant);
end loop;
end;
/
show errors
/*=== TESTING loopy ===*/
prompt ============
prompt TESTING loopy
prompt
prompt ============
prompt testing loopy(5):
prompt should see:
prompt ======
prompt BURP! followed by 0-4,
prompt GLIP! followed by 13-16,
prompt all department names and locations,
prompt a message that King has a salary 1000 times 5,
prompt then i has value: followed by 100-103
prompt ============
exec loopy(5)
prompt ============
prompt testing loopy(2):
prompt should see:
prompt ======
prompt BURP! followed by 0-1,
prompt GLIP! followed by 13-16,
prompt all department names and locations,
prompt a message that no employee has a salary 1000 times 2
prompt (i loop is never reached because exception raised)
prompt ============
exec loopy(2)
prompt ============
prompt testing loopy(1.25):
prompt should see:
prompt ======
prompt BURP! followed by 0-1,
prompt GLIP! followed by 13-16,
prompt all department names and locations,
prompt messages that Ward and Martin have a salary 1000 times 1.25
prompt (i loop is never reached because exception raised)
prompt ============
exec loopy(1.25)
/*=====
procedure: reverse_play: void -> void
purpose: expects nothing, and prints to the screen
the results from running 4 for-loops,
two with reverse, two without, and flipping
the order of the start and end values
example:
when run, this SHOULD print:
i: 2
i: 3
i: 4
i: 5
j: 5
j: 4
j: 3
j: 2
====*/
create or replace procedure reverse_play as
begin
-- example 1: typical classic for-loop in PL/SQL
for i in 2 .. 5 loop
dbms_output.put_line('i: ' || i);
end loop;
-- example 2: REVERSE with ascending start and end values
dbms_output.put(chr(10));
for j in REVERSE 2 .. 5 loop
dbms_output.put_line('j: ' || j);
end loop;
-- example 3: REVERSE with descending start and end values
-- (turns out, this simply never enters the loop,
-- because 2 is not greater than 5?)
dbms_output.put(chr(10));
for k in REVERSE 5 .. 2 loop
dbms_output.put_line('k: ' || k);
end loop;
-- example 4: what if NO reverse, and descending start and end
-- values?
-- (turns out, this simply never enters the loop,
-- because 2 is not greater than 5?)
dbms_output.put(chr(10));
for m in 5 .. 2 loop
dbms_output.put_line('m: ' || m);
end loop;
end;
/
show errors
/*=== TESTING reverse_play ===*/
prompt ============
prompt TESTING reverse_play
prompt
prompt ============
prompt testing reverse_play():
prompt should see i from 2 to 5,
prompt j from 5 to 2,
prompt and that is all:
prompt ============
exec reverse_play()