07.user Defined Records-Functins and Procedures
07.user Defined Records-Functins and Procedures
(6-1)
Copy and execute the following anonymous block. Then modify it to declare and use a single
record instead of a scalar variable for each column. Make sure that your code will still work if an
extra column is added to the departments table later. Execute your modified block and save your
code.
declare
v_dept_id DEPARTMENTS.DEPARTMENT_ID%type;
v_dept_name DEPARTMENTS.DEPARTMENT_NAME%type;
v_mgr_id DEPARTMENTS.MANAGER_ID%type;
v_loc_id dEPARTMENTS.LOCATION_ID%type;
begin
select DEPARTMENT_ID
, DEPARTMENT_NAME
, MANAGER_ID
, LOCATION_ID
into v_dept_id
, v_dept_name
, v_mgr_id, v_loc_id
from DEPARTMENTS
where DEPARTMENT_ID = 80;
dbms_output.put_line(v_dept_id || ' '
|| v_dept_name || ' '
|| v_mgr_id || ' ' || v_loc_id);
exception
when no_data_found then
dbms_output.put_line('This department does not exist');
end;
Source Code:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(6-2.1b)
Which of the following are collections and which are not?
1. A list of all employees’ last names
2. The character value “Chang”
3. The populations of all countries in Europe
4. All the data stored in the employees table about a specific employee.
Answer
(6-2.1c)
What is the difference between an INDEX BY table and a database table such as EMPLOYEES or
COUNTRIES?
Answer:
(6-2.1d)
Describe the difference between an INDEX BY table and an INDEX BY table of records.
Answer:
(6-2.1e)
Look at the following code. Describe the difference between t_pops and v_pops_tab. Is v_pops_tab
an INDEX BY table or an INDEX BY table of records? How do you know?
declare
type t_pops is table of COUNTRIES.POPULATION%TYPE
index by binary_integer;
v_pops_tab t_pops;
Answer:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(6-2.2a)
Write and execute an anonymous block that declares and populates an INDEX BY table of
countries in South America (region_id = 5). The table should use country_id as a primary key, and
should store the country names as the element values. The data should be stored in the table in
ascending sequence of country_id. The block should not display any output. Save your code.
Source Code:
(6-2.2b)
Modify the block so that after populating the INDEX BY table, it uses a FOR loop to display the
contents of the INDEX BY table. You will need to use the FIRST, LAST, and EXISTS table
methods. Execute the block and check the displayed results. Save your code.
Source Code:
(6-2.2c)
Modify the block again so that instead of displaying all the contents of the table, it displays only
the first and last elements and the number of elements in the INDEX BY table. Execute the block
and check the displayed results.
Source Code:
(6-2.3a)
Write and execute an anonymous block that declares and populates an INDEX BY table of records
containing employee data. The table of records should use the employee id as a primary key, and
each element should contain an employee’s last name, job id, and salary. The data should be stored
in the INDEX BY table of records in ascending sequence of employee id. The block should not
display any output.
Hint: declare a cursor to fetch the employee data, then declare the INDEX BY table as cursor name
%ROWTYPE. Save your code.
Source Code:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(6-2.3b)
Modify the block so that after populating the table of records, it uses a FOR loop to display to
display the contents. You will need to use the FIRST, LAST and EXISTS table methods. Execute
the block and check the displayed results. Save your code.
Source Code:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(8-1.4a)
Use the code below to create a procedure in Oracle SQL Developer. Save the definition of your
procedure in case you need to modify it later.
(8-1.4b)
Execute the procedure by running the following anonymous block:
set serveroutput on
begin
name_change;
print_line('8-1 Q4b done');
end;
(8-1.4c)
SELECT from the table to check that the procedure has executed correctly and performed the
UPDATE.
Screen shot:
(8-1.5)
Create a second procedure named pay_raise which changes the salary of all employees in
EMPLOYEES_DUP to a new value of 30000. Execute the procedure from an anonymous block,
then SELECT from the table to check that the procedure has executed correctly.
Code:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(8-1.6)
Retrieve your first name_change procedure by clicking on its name in the Saved SQL window.
Modify the code to remove or replace from the create statement, and introduce a deliberate error
into the code, for example by misspelling a keyword: UPDAT employees_dup. Execute your code
to recreate the procedure. What happens?
Answer:
(8-1.7)
Now correct the procedure code by reinserting the OR REPLACE clause and correcting your
deliberate spelling error. Execute your code to recreate the procedure. Now what happens?
Answer:
(8-1.8)
Create, save, and execute a procedure which updates the salary of employees in employees_dup
according to the following rules:
- if the employee is in department 80, the new salary = 1000
- if the employee is in department 50, the new salary = 2000
- if the employee is in any other department, the new salary = 3000.
You will need to include three UPDATE statements, one for each of the above rules. In a later
lesson you will learn how to avoid this. Execute your procedure from an anonymous block and
verify that the updates have been performed correctly.
Code:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
6
academy.oracle.com
Database Programming with PL/SQL
7.4 Using Parameters in Procedures
(8-2.2b)
Execute your procedure from an anonymous block, using country_id 90.
Screen Shot:
(8-2.2c)
Re-execute the procedure from the anonymous block, this time using country_id 95. What happens?
Screens shot:
Answer:
(8-2.2d)
Retrieve your procedure code from Saved SQL and modify it to trap the NO_DATA_FOUND
exception in an exception handler. Execute the modified procedure using country_id 95 again.
Now what happens?
Screens Shot:
Answer:
(8-2-3)
In your own words, describe what a formal parameter is and what an actual parameter is. Also,
name three variations for an actual parameter.
Answer:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(8-2.4a)
Write a procedure that displays the number of countries in a given region whose highest elevations
exceed a given value. The procedure should accept two formal parameters, one for a region_id
and the other for an elevation value for comparison. Use print_line to display the results in a
message. Save your procedure code.
Code:
Screen Shot:
(8-2.4b)
Execute your procedure using the value 5 for the region_id and 2000 for the highest elevation.
Screen shot:
(8-2.4c)
DESCRIBE your procedure to check the names and datatypes of its formal parameters.
Screen Shot:
(8-2.4d)
Retrieve your procedure code from Saved SQL and modify it to accept a third formal parameter of
datatype CHAR. The procedure should display a count of the number of countries in a given
region whose highest elevations exceed a given value and whose country name starts with a
given alphabetic character. Your SELECT statement should include a WHERE condition to
compare the first character of each country’s name with the third parameter value (Hint: use
SUBSTR). Save your work again and DESCRIBE the modified procedure.
Code:
Description:
(8-2.4e)
Write an anonymous block which declares three variables to store actual parameter values for the
region_id, elevation, and area, and then executes the procedure passing these values. Execute
the block using values 5, 2000, and ‘B’.
Code:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(8-2.4f)
Modify your anonymous block to use the same actual parameter values but pass them to the
procedure in a different order: (5, ‘B’, 2000). Execute the block. What happens and why?
Code:
Screen Shot:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(8-3.2a)
Create a procedure that receives a country_id as an IN parameter and returns the name and
population of that country as OUT parameters. Include an exception handler to trap the
NO_DATA_FOUND exception if the country does not exist. The procedure should not display the
returned values; this will be done in the next step. Name your procedure find_area_pop. Save
your code.
Code:
(8-3.2b)
Test your procedure by creating and executing an anonymous block which invokes the procedure
and displays the returned OUT values. Save your code. Run the block twice, with country_ids 2
(Canada) and 10 (does not exist).
Screen Shot:
(8-3.2c)
Retrieve your procedure code and modify it to add a third OUT parameter which is the population
density of the country, using the formula: density = (population / area). You will need to modify
your SELECT statement to fetch the area column value into a local variable. Save your modified
code.
Code:
(8-3.3d)
Test your modified procedure using country_id 2. You will need to modify your calling anonymous
block to declare and pass a fourth actual parameter to receive the population density from the
procedure. Save your code.
Screen Shot:
(8-3.3)
Create a procedure which accepts an integer as an IN OUT parameter and returns the square of
that integer, for example the square of 4 is 16. Save your code. Test your procedure from an
anonymous block three times, using integer values 4, 7, and –20 (negative 20).
Code:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(8-3.4)
List the three methods of passing parameters to a procedure.
Answer:
(8-3.4a)
Retrieve your anonymous block from question 2D and modify its call to find_area_pop to pass the
four parameters using named notation. Test your block, again using country_id 2 (Canada). If you
have forgotten the p_ names of the procedure’s formal parameters, how can you refresh your
memory?
Code:
(8-3.4b)
Modify the anonymous block from the previous step to pass the FIRST two parameters using
named notation and the LAST two using positional notation. Test the block again. What happens?
Code:
Screen Shot:
(8-3.4c)
Correct the problem in the previous step by modifying the anonymous block again to pass the first
two parameters using positional notation and the last two using named notation. Test the block
again.
Screen Shot:
(8-3.5)
In your own words, describe the purpose of the DEFAULT option for parameters and state the two
syntax options for providing the default value in the procedure header.
Answer:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(8-3.6)
Find the country_id of your own country by executing a suitable SELECT…FROM countries….
Then retrieve your find_area_pop procedure from question 2C. Modify the code to use your
country_id as a default value for the country_id IN parameter. Save your code. Then retrieve your
anonymous block from question 2D and modify it so that it does NOT pass the country_id to the
procedure. Test the block and check that your country’s details are returned and displayed. If your
modified anonymous block does not work, correct it so it will.
Working Code:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(9-1.2)
Create a function called full_name. Pass two parameters to the function, an employee’s last name
and first name. The function should return the full name in the format, last name, comma, space,
first name (for example: Smith, Joe). Save your code.
Code:
(9-1.2a)
Test your function from an anonymous block which uses a local variable to store and display the
returned value.
Code of anonymous block:
(9-1.2b)
Modify your anonymous block from the previous step to remove the local variable declaration and
call the function directly from within the print_line call. Test the block again.
Code of anonymous block:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(9-1.2c)
Now call the function from within a SELECT statement, not a PL/SQL block. Your SELECT
statement should display the first_name, last_name, and full name (using the function) of all
employees in department 50. Your output should look like this:
Code for Select:
Screens shot:
(9-1.3)
Create a function called divide that accepts two numbers as input and returns the result of dividing
the first number by the second number, rounded to two decimal places. Save your code.
Code:
(9-1.3a)
Test your function twice from an anonymous block using input values (50, 2) and (25, 3).
Screen Shot of tests:
(9-1.3b)
Test your function a third time using input values (16, 0).
What happens?
(9-1.3c)
Modify the function code to trap the ZERO_DIVIDE exception. The exception handler should
return a value of zero from the function if ZERO_DIVIDE is raised.
Code:
(9-1.3d)
Test your function again using input values (16,0) as before.
Now what happens?
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(9-1.4)
List four major differences between a procedure and a function.
Answer:
(9-1.5)
Look at the following two subprograms. The first is a procedure; the second is a function. Answer
the following questions.
create or replace
procedure get_country_name_proc (p_country_id in COUNTRIES.COUNTRY_ID
%type,
p_country_name out
COUNTRIES.COUNTRY_NAME%type
) is
begin
select COUNTRY_NAME
into p_country_name
from COUNTRIES
where COUNTRY_ID = p_country_id;
end;
create or replace
function get_country_name_func (p_country_id in COUNTRIES.COUNTRY_ID
%type)
return varchar2 is
v_country_name COUNTRIES.COUNTRY_NAME%type;
begin
select COUNTRY_NAME
into v_country_name
from COUNTRIES
where COUNTRY_ID = p_country_id;
return v_country_name;
end;
(9-1.5a)
For a given country id, will both of these subprograms return the same results?
(9-1.5b)
What is the advantage of creating this subprogram as a function rather than as a procedure?
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(9-1.5c)
Which of the following procedure and function calls are valid and which are not? Explain why the
invalid ones will fail.
declare
v_country_id COUNTRIES.COUNTRY_ID%type := 2;
v_country_name COUNTRIES.COUNTRY_NAME%type;
begin
get_country_name_proc(v_country_id, v_country_name); -- call 1
v_country_name := get_country_name_func(v_country_id); -- call 2
v_country_name := get_country_name_proc(v_country_id); -- call 3
end;
Answer:
CALL # Is Valid? If invalid, Why does it fail?
Call 1
Call 2
Call 3
Call 4
Call 5
(9-1.6)
List the ways you can invoke (i.e., call) a function.
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(9-1.7)
Create a function which accepts a character string as input and returns the same character string
but with the order of the letters reversed. For example, "Smith" would be returned as "htimS." Save
your code. Hint: you will need to declare a local variable to store the reversed string, and build its
contents by reading the input one character at a time (using SUBSTR) in a loop structure, starting
from the last character. Each execution of the loop reads the preceding character and
concatenates it to the reversed string.
Code:
(9-1.8)
Test your function using the following SQL statements:
select LAST_NAME, reverse_string(LAST_NAME)
from EMPLOYEES;
select COUNTRY_NAME
, reverse_string(COUNTRY_NAME)
from COUNTRIES;
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(9-2.1)
Create and execute a function sal_increase using the following two code samples. The first
creates a function which returns an employee’s new salary if a percentage increase is granted.
The second calls this function in a SELECT statement, using an increase of 5 percent.
create or replace
function sal_increase(p_salary F_EMPS.SALARY%type,
p_percent_incr number)
return number is
begin
return (p_salary + (p_salary * p_percent_incr / 100));
end;
select LAST_NAME
, SALARY
, sal_increase(SALARY, 5)
from F_EMPS;
Now, suppose you want to see the same information in your SELECT statement, but only for those
employees for whom the increased salary would be greater than 10000. Write and test two
SELECT statements to do this. In the first, do NOT use your function. In the second, use your
function. Use an increase of 5 percent.
Code:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
Screen Shot:
Which do you think is better, and why?
(9-2.2)
Name five places within a SQL statement where a function can be used. The first one has been
done for you (think of four more).
(9-2.3)
Modify your anonymous block from question 1 (the block with the calls to the sal_increase
function) to ORDER the results by the increased salary in descending order (i.e., highest
increased salary first).
Code:
Screens Shot:
(9-2.4)
Examine the following SELECT statement which lists the total salaries in each department for
those departments whose total salary is greater than 20000.
select DEPARTMENT_ID
, sum(SALARY)
from F_EMPS
group by DEPARTMENT_ID
having sum(SALARY) > 20000;
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
Modify the statement so that it also lists the total salary in each department if a 5 percent increase
is granted, and lists those departments whose increased total salary would be greater than 20000.
Your modified statement should call the sal_increase function twice, once in the column_list and
once in the HAVING clause. Test the modified statement.
Modified Code:
(9-2.5)
The following function accepts a department id as an input parameter and checks whether the
department exists in the f_depts table. Run this code to create the check_dept function.
create or replace function check_dept(p_dept_id F_DEPTS.DEPARTMENT_Id
%type)
return boolean is
v_dept_id F_DEPTS.DEPARTMENT_ID%type;
begin
select department_id
into v_dept_id
from f_depts
where department_id = p_dept_id;
return true;
exception
when no_data_found then
return false;
end;
Examine the above function and explain why it could not be used within a SQL statement. Could
this function be used within a PL/SQL statement? Why or why not?
(9-2.5)
Write a procedure called insert_emp which inserts a new employee into f_emps. Pass the
employee id, last name, salary, and department id to the procedure as IN parameters. The
procedure should call your check_dept function to verify that the passed department id exists in
the f_depts table. If it exists, insert the employee. If it does not exist, use print_line to display a
suitable error message. Save your code.
Code:
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(9-2.7)
Test your insert_emp procedure from an anonymous block using the following IN parameter
values: employee_id = 800, last_name = Jokinen, salary = 5000, and department_id = 750. What
happened and why?
Screen Shot and Answer to Question:
(9-2.8)
Modify your insert_emp : so that if the department does not exist, the procedure first inserts a new
department with the non-existent department id and a department name of ‘Temporary’, and then
inserts the employee. Test your procedure again with the same IN values used in the previous
question.
Code:
(9-2.9)
Execute two SELECT statements to confirm department id 750 and employee id 800 were added
to the F_DEPTS and F_EMPS tables, respectively.
Screen Shot:
(9-2.10)
Create the function get_sal using the following code:
create or replace function get_sal (p_emp_id f_emps.employee_id%type)
return number is
v_salary f_emps.salary%type;
begin
select salary
into v_salary
from f_emps
where employee_id = p_emp_id;
return v_salary;
end;
Use the get_sal function in the following SQL statement (which attempts to move all higher
salaried employees to department 50).
update F_EMPS
set DEPARTMENT_ID = 50
where get_sal(EMPLOYEE_ID) > 10000;
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(9-2.10)
Examine the following function (which doubles the salary of a chosen employee) and the SQL
statement which uses it.
v_salary := v_salary * 2;
update F_EMPS
set SALARY = v_salary
where EMPLOYEE_ID = p_emp_id;
return v_salary;
end;
select EMPLOYEE_ID
, last_name, salary, upd_sal(employee_id)
from F_EMPS
where EMPLOYEE_ID = 100;
Create the upd_sal function then run the SELECT statement to confirm your prediction.
Was your predictin correct? (provide a screen shot with your answer):
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
(9-2.11)
Examine the following function (which doubles the salary of a chosen employee) and the SQL
statement which uses it.
v_salary := v_salary * 2;
update F_EMPS
set SALARY = v_salary
where EMPLOYEE_ID = p_emp_id;
return v_salary;
end;
select EMPLOYEE_ID
, LAST_NAME
, SALARY
, upd_sal(EMPLOYEE_ID)
from F_EMPS
where EMPLOYEE_ID = 100;
Create the upd_sal function, then run the SELECT statement to confirm your prediction (include a
screens shot):
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners
Copyright © 2019, Oracle and/or its affiliates. All rights reserved. Oracle and Java are registered trademarks of Oracle and/or its affiliates. Other names may be trademarks of their respective owners