Третий Блок
Практическое занятие 9. Создание процедур
- Создание процедур.
- Режимы параметров.
Задание 1. Создайте процедуру add_job для добавления записей в таблицу Jobs.
- Процедура должна принимать на вход 2 параметра —
idиjob_title. - Запустите процедуру на исполнение с помощью анонимного блока или инструкции
EXECUTE. Протестируйте работу на примере следующих значений, зафиксируйте и объясните результат: IT_DBAиDatabase Administrator.- Запустите процедуру на исполнение с помощью анонимного блока или инструкции
EXECUTE. Протестируйте работу на примере следующих значений, зафиксируйте и объясните результат: ST_MANиStock Manager.
CREATE OR REPLACE PROCEDURE
add_job (id Jobs.Job_ID%TYPE, job_title Jobs.Job_Title%TYPE) IS
BEGIN
INSERT INTO Jobs (Job_ID, Job_Title)
VALUES (id, job_title);
END add_job;Procedure ADD_JOB compiled
EXECUTE add_job('IT_DBA', 'Database Administrator')PL/SQL procedure successfully completed.Всё прошло успешно, так как в таблице Jobs нет записи с ключом IT_DBA.
EXECUTE add_job('ST_MAN', 'Stock Manager')Error starting at line : 1 in command -
BEGIN add_job('ST_MAN', 'Stock Manager'); END;
Error report -
ORA-00001: нарушено ограничение уникальности (RUMINAT.JOB_ID_PK)
ORA-06512: на "RUMINAT.ADD_JOB", line 4
ORA-06512: на line 1
00001. 00000 - "unique constraint (%s.%s) violated"
*Cause: An UPDATE or INSERT statement attempted to insert a duplicate key.
For Trusted Oracle configured in DBMS MAC mode, you may see
this message if a duplicate entry exists at a different level.
*Action: Either remove the unique restriction or do not insert the key.Мы получили ошибку, так как в таблице Jobs уже есть запись с ключом ST_MAN.
Задание 2. Создайте процедуру upd_job для изменения значения в таблице Jobs.
- Процедура должна принимать на вход параметр
idи новое значение для столбцаJob_Title. Процедура должна оповещать пользователей, в случае если никаких модификаций не было сделано. Для этого воспользуйтесь атрибутомSQL%FOUNDи инструкциейRAISE_APPLICATION_ERRORилиDBMS_OUTPUT.PUT_LINE. - Запустите процедуру на исполнение и поменяйте с ее помощью
Job_Titleдля записиIT_DBAнаData Administrator. Сделайте выборку из таблицыJobs, чтобы убедиться в модификации данных. - Протестируйте работу процедуры в случае получения на вход несуществующего
Job_ID— к примеру,IT_WEB.
CREATE OR REPLACE PROCEDURE
upd_job (id Jobs.Job_ID%TYPE, jobTitle Jobs.Job_Title%TYPE) IS
BEGIN
UPDATE Jobs
SET Job_Title = jobTitle
WHERE Job_ID = id;
IF NOT SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('upd_job error:');
DBMS_OUTPUT.PUT_LINE(
' there were no modifications applied for'
|| ' upd_job(''' || id || ''', ''' || jobTitle || ''').'
);
DBMS_OUTPUT.PUT_LINE(' There is no job with ID ''' || id || '''.');
END IF;
END upd_job;Procedure UPD_JOB compiled
EXECUTE upd_job ('IT_DBA', 'Data Administrator')
SELECT * FROM Jobs WHERE Job_ID = 'IT_DBA';| JOB_ID | JOB_TITLE | MIN_SALARY | MAX_SALARY |
|---|---|---|---|
| IT_DBA | Data Administrator | (null) | (null) |
EXECUTE upd_job ('IT_WEB', 'Web developer')upd_job error:
there were no modifications applied for upd_job('IT_WEB', 'Web developer').
There is no job with ID 'IT_WEB'.
Задание 3. Создайте процедуру del_job для удаления записей из таблицы Jobs.
- Создайте процедуру с одним входным параметром —
id. Создайте в процедуре обработчик на случай, если ни одна запись не будет удалена. - Протестируйте работу процедуры: вызовите её на исполнение и попытайтесь удалить существующую запись с
Job_ID = IT_DBA. - Протестируйте работу процедуры: вызовите её на исполнение и попытайтесь с ее помощью удалить несуществующую запись, к примеру с
Job_ID = IT_WEB. Зафиксируйте сообщение об ошибке.
CREATE OR REPLACE PROCEDURE
del_job (id Jobs.Job_ID%TYPE) IS
BEGIN
DELETE FROM Jobs WHERE Job_ID = id;
IF NOT SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('del_job error:');
DBMS_OUTPUT.PUT_LINE(
' there were no records deleted by'
|| ' del_job(''' || id || ''').'
);
DBMS_OUTPUT.PUT_LINE(' There is no job with ID ''' || id || '''.');
END IF;
END del_job;Procedure DEL_JOB compiled
EXECUTE del_job ('IT_DBA')
SELECT * FROM Jobs WHERE Job_ID = 'IT_DBA';| JOB_ID | JOB_TITLE | MIN_SALARY | MAX_SALARY |
|---|
EXECUTE del_job ('IT_WEB')del_job error:
there were no records deleted by del_job('IT_WEB').
There is no job with ID 'IT_WEB'.
Задание 4. Создайте процедуру get_employee для выборки из таблицы Employees значений Job_ID и Salary указанного сотрудника.
- Процедура должна принимать на вход
Employee_IDи возвращать с помощьюOUTпараметров значенияJob_IDиSalary. - Протестируйте работу процедуры передавая на вход локальные переменные блока вызова или хост-переменные. Просмотрите зарплату и должность для
120сотрудника. - Просмотрите зарплату и должность для
300сотрудника.
CREATE OR REPLACE PROCEDURE
get_employee (
id Employees.Employee_ID%TYPE,
jobID OUT Employees.Job_ID%TYPE,
empSalary OUT Employees.Salary%TYPE
) IS
BEGIN
SELECT Job_ID, Salary
INTO jobID, empSalary
FROM Employees
WHERE Employee_ID = id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('get_employee error:');
DBMS_OUTPUT.PUT_LINE(
' could not get Employee information from'
|| ' get_employee(' || id || ', jobID, empSalary).'
);
DBMS_OUTPUT.PUT_LINE(' There is no employee with ID ' || id || '.');
END get_employee;Procedure GET_EMPLOYEE compiledDECLARE
jobID Employees.Job_ID%TYPE;
empSalary Employees.Salary%TYPE;
BEGIN
get_employee(120, jobID, empSalary);
DBMS_OUTPUT.PUT_LINE(
'Employee #120: Job_ID = ' || NVL(jobID, '(null)')
|| ', Salary = ' || NVL(TO_CHAR(empSalary), '(null)') || '.'
);
get_employee(300, jobID, empSalary);
DBMS_OUTPUT.PUT_LINE(
'Employee #300: Job_ID = ' || NVL(jobID, '(null)')
|| ', Salary = ' || NVL(TO_CHAR(empSalary), '(null)') || '.'
);
END;Employee #120: Job_ID = ST_MAN, Salary = 8000.
get_employee error:
could not get Employee information from get_employee(300, jobID, empSalary).
There is no employee with ID 300.
Employee #300: Job_ID = (null), Salary = (null).Практическое занятие 10. Создание функций
- Создание функций.
- Уровень чистоты.
Задание 1. Создайте функцию get_job, которая возвращает Job_Title.
- Создайте функцию
get_job, которая возвращаетJob_Title, соответствующий переданному на входJob_ID. - Для тестирования работы функции создайте анонимный блок, объявите хост-переменную
b_titleтипаVARCHAR2, сохраните в нее результат работы функции и выведите результат работы функции для значения входного параметраSA_REP.
CREATE OR REPLACE FUNCTION
get_job (id Jobs.Job_ID%TYPE)
RETURN Jobs.Job_Title%TYPE IS
title Jobs.Job_Title%TYPE;
BEGIN
SELECT Job_Title
INTO title
FROM Jobs
WHERE Job_ID = id;
RETURN title;
END get_job;Function GET_JOB compiled
VARIABLE b_title VARCHAR2;
DECLARE
id Jobs.Job_ID%TYPE := 'SA_REP';
BEGIN
:b_title := get_job(id);
DBMS_OUTPUT.PUT_LINE('get_job(''' || id || ''') = ' || :b_title);
END;get_job('SA_REP') = Sales Representative
Задание 2. Создайте функцию get_annual_comp для формирования фактической зарплаты сотрудника по заданной формуле.
- Формула:
(salary * 12) + (commission_pct * salary * 12). Функция должна принимать на вход месячную зарплату сотрудника и комиссию, и даже в случае если оба значения будутNULLвозвращать неNULL. - Протестируйте работу функции с помощью оператора
SELECTдля сотрудников из30отдела.
CREATE OR REPLACE FUNCTION
get_annual_comp (
sal Employees.Salary%TYPE,
pct Employees.Commission_PCT%TYPE
)
RETURN NUMBER IS
BEGIN
RETURN (NVL(sal, 0) * 12) + (NVL(pct, 0) * NVL(sal, 0) * 12);
END get_annual_comp;Function GET_ANNUAL_COMP compiled
SELECT
Last_Name AS "Employee" ,
Salary AS "Salary" ,
NVL(TO_CHAR(Commission_PCT), ' ') AS "Commission_PCT",
get_annual_comp(Salary, Commission_PCT) AS "Result"
FROM Employees
WHERE Department_ID = 30;| Employee | Salary | Commission_PCT | Result |
|---|---|---|---|
| Raphaely | 11000 | 132000 | |
| Khoo | 3100 | 37200 | |
| Baida | 2900 | 34800 | |
| Tubias | 2800 | 33600 | |
| Himuro | 2600 | 31200 | |
| Colmenares | 2500 | 30000 |
Задание 3. Создайте процедуру add_employee для добавления нового сотрудника в табличку Employees. Перед добавлением сотрудника процедура должна проверять с помощью функции valid_deptid, что указанный для него номер отдела присутствует в таблице Departments.
- Создайте функцию
valid_deptid, которая принимает на вход номер отдела и возвращаетTRUEилиFALSE, в зависимости от того есть или нет такой отдел в таблицеDepartments. - Создайте процедуру
add_employee, которая добавляет запись в таблицуEmployeesтолько в том случае, если функцияvalid_deptidвернулаTRUE, иначе выдает соответствующее сообщение об ошибке. Процедура принимает на вход следующие параметры и присваивает им указанные в скобках значения по умолчанию:first_name,last_name,e-mail,job (SA_REP),mgr (145),sal (1000),comm (0),deptid (30).
Employees_Seq. Установите значение в столбцеHire_Dateс помощью функцииTRUNC(SYSDATE). - Протестируйте работу процедуры для сотрудника
Jane Harrisиз15отдела с e-mail-омJAHARRIS(остальные значения оставьте по умолчанию). Зафиксируйте и объясните результат. - Протестируйте работу процедуры для сотрудника
Joe Harrisиз80отдела с e-mail-омJHARRIS(остальные значения оставьте по умолчанию). Зафиксируйте и объясните результат.
CREATE OR REPLACE FUNCTION
valid_deptid (depID Departments.Department_ID%TYPE)
RETURN BOOLEAN IS
BEGIN
FOR i IN (
SELECT 'Exists' FROM dual
WHERE EXISTS (
SELECT Department_ID
FROM Departments
WHERE Department_ID = depID
)
)
LOOP
RETURN TRUE;
END LOOP;
RETURN FALSE;
END valid_deptid;
/
CREATE OR REPLACE PROCEDURE
add_employee (
firstName Employees.First_Name%TYPE
, lastName Employees.Last_Name%TYPE
, mail Employees.Email%TYPE
, job Employees.Job_ID%TYPE := 'SA_REP'
, mgr Employees.Manager_ID%TYPE := 145
, sal Employees.Salary%TYPE := 1000
, comm Employees.Commission_PCT%TYPE := 0
, depID Employees.Department_ID%TYPE := 30
) IS
BEGIN
IF valid_deptid(depID) THEN
INSERT INTO Employees (
Employee_ID
, First_Name
, Last_Name
, Email
, Hire_Date
, Job_ID
, Manager_ID
, Salary
, Commission_PCT
, Department_ID
)
VALUES (
Employees_Seq.NEXTVAL -- Employee_ID
, firstName
, lastName
, mail
, TRUNC(SYSDATE) -- Hire_Date
, job
, mgr
, sal
, comm
, depID
);
ELSE
DBMS_OUTPUT.PUT_LINE(
'Error: cannot add an employee to a nonexistent department #' || depID || '.'
);
END IF;
END add_employee;
/Function VALID_DEPTID compiled
Procedure ADD_EMPLOYEE compiled
EXECUTE add_employee('Jane', 'Harris', 'JAHARRIS', depID => 15)Error: cannot add an employee to a nonexistent department #15.
Ничего не получилось, потому что не существует 15-го отдела.
EXECUTE add_employee('Joe', 'Harris', 'JHARRIS', depID => 80)
SELECT * FROM Employees WHERE Last_Name = 'Harris'| EMPLOYEE_ID | FIRST_NAME | LAST_NAME | PHONE_NUMBER | HIRE_DATE | JOB_ID | SALARY | COMMISSION_PCT | MANAGER_ID | DEPARTMENT_ID | |
|---|---|---|---|---|---|---|---|---|---|---|
| 207 | Joe | Harris | JHARRIS | (null) | 26.03.19 | SA_REP | 1000 | 0 | 145 | 80 |
Теперь мы добавляем в существующий отдел, поэтому всё хорошо.
Практическое занятие 12. Создание пакетов
- Создание пакетов.
- Элементы пакета.
privateиpublicконструкции пакета.
Задание 1. Создайте спецификацию и тело пакета job_pkg, и сохраните в нем созданные ранее процедуры и функции add_job, upd_job, del_job, get_job. Сохраните код создания спецификации и тела пакета в разных файлах *.sql. Для упрощения отладки включите инструкцию show errors в каждый скрипт.
- Создайте спецификацию пакета. Объявите публичные элементы.
- Создайте тело пакета. Включите в него реализацию процедур и функций.
- Удалите ранее созданные (независимые) процедуры и функции:
add_job,upd_job,del_job,get_job. - Протестируйте работу пакета. Запустите на исполнение процедуру
job_pkg.add_jobпередав в качестве значений параметров: IT_SYSAN,Systems analyst.- Сделайте выборку из таблицы
Jobs, чтобы просмотреть результат работы процедуры.
CREATE OR REPLACE PACKAGE job_pkg IS
PROCEDURE add_job (id Jobs.Job_ID%TYPE, job_title Jobs.Job_Title%TYPE);
PROCEDURE upd_job (id Jobs.Job_ID%TYPE, jobTitle Jobs.Job_Title%TYPE);
PROCEDURE del_job (id Jobs.Job_ID%TYPE);
FUNCTION get_job (id Jobs.Job_ID%TYPE) RETURN Jobs.Job_Title%TYPE;
END job_pkg;Package JOB_PKG compiledCREATE OR REPLACE PACKAGE BODY job_pkg IS
PROCEDURE add_job (id Jobs.Job_ID%TYPE, job_title Jobs.Job_Title%TYPE) IS
BEGIN
INSERT INTO Jobs (Job_ID, Job_Title)
VALUES (id, job_title);
END add_job;
PROCEDURE upd_job (id Jobs.Job_ID%TYPE, jobTitle Jobs.Job_Title%TYPE) IS
BEGIN
UPDATE Jobs
SET Job_Title = jobTitle
WHERE Job_ID = id;
IF NOT SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('upd_job error:');
DBMS_OUTPUT.PUT_LINE(
' there were no modifications applied for'
|| ' upd_job(''' || id || ''', ''' || jobTitle || ''').'
);
DBMS_OUTPUT.PUT_LINE(' There is no job with ID ''' || id || '''.');
END IF;
END upd_job;
PROCEDURE del_job (id Jobs.Job_ID%TYPE) IS
BEGIN
DELETE FROM Jobs WHERE Job_ID = id;
IF NOT SQL%FOUND THEN
DBMS_OUTPUT.PUT_LINE('del_job error:');
DBMS_OUTPUT.PUT_LINE(
' there were no records deleted by'
|| ' del_job(''' || id || ''').'
);
DBMS_OUTPUT.PUT_LINE(' There is no job with ID ''' || id || '''.');
END IF;
END del_job;
FUNCTION get_job (id Jobs.Job_ID%TYPE)
RETURN Jobs.Job_Title%TYPE IS
title Jobs.Job_Title%TYPE;
BEGIN
SELECT Job_Title
INTO title
FROM Jobs
WHERE Job_ID = id;
RETURN title;
END get_job;
END job_pkg;Package Body JOB_PKG compiled
DROP PROCEDURE add_job;
DROP PROCEDURE upd_job;
DROP PROCEDURE del_job;
DROP FUNCTION get_job;Procedure ADD_JOB dropped.
Procedure UPD_JOB dropped.
Procedure DEL_JOB dropped.
Function GET_JOB dropped.
EXECUTE job_pkg.add_job('IT_SYSAN', 'Systems analyst')
SELECT * FROM Jobs WHERE Job_ID = 'IT_SYSAN'| JOB_ID | JOB_TITLE | MIN_SALARY | MAX_SALARY |
|---|---|---|---|
| IT_SYSAN | Systems analyst | (null) | (null) |
Задание 2. Создайте пакет, состоящий из public и private конструкций.
- Создайте спецификацию и тело пакета
emp_pkg, в котором естьpublicпроцедурыadd_employeeиget_employee, иprivateфункцияvalid_deptid. - Протестируйте работу процедуры
emp_pkg.add_employee. Добавьте в таблицу информацию о сотрудникеJane Harrisиз15отдела, с e-mail-омJAHARRIS. Вы должны получить сообщение об ошибке, потому что 15 отдела не существует. - Запустите процедуру еще раз и добавьте информацию о
David Smithиз80отдела, с e-mail-омDASMITH. - Сделайте выборку из таблицы
Employees, чтобы убедиться в корректном добавлении новых сотрудников.
CREATE OR REPLACE PACKAGE emp_pkg IS
PROCEDURE add_employee (
firstName Employees.First_Name%TYPE
, lastName Employees.Last_Name%TYPE
, mail Employees.Email%TYPE
, job Employees.Job_ID%TYPE := 'SA_REP'
, mgr Employees.Manager_ID%TYPE := 145
, sal Employees.Salary%TYPE := 1000
, comm Employees.Commission_PCT%TYPE := 0
, depID Employees.Department_ID%TYPE := 30
);
PROCEDURE get_employee (
id Employees.Employee_ID%TYPE
, jobID OUT Employees.Job_ID%TYPE
, empSalary OUT Employees.Salary%TYPE
);
END emp_pkg;Package EMP_PKG compiledCREATE OR REPLACE PACKAGE BODY emp_pkg IS
/*
--- PRIVATE ---
*/
FUNCTION valid_deptid (depID Departments.Department_ID%TYPE)
RETURN BOOLEAN IS
BEGIN
FOR i IN (
SELECT 'Exists' FROM dual
WHERE EXISTS (
SELECT Department_ID
FROM Departments
WHERE Department_ID = depID
)
)
LOOP
RETURN TRUE;
END LOOP;
RETURN FALSE;
END valid_deptid;
/*
--- PUBLIC ---
*/
PROCEDURE add_employee (
firstName Employees.First_Name%TYPE
, lastName Employees.Last_Name%TYPE
, mail Employees.Email%TYPE
, job Employees.Job_ID%TYPE := 'SA_REP'
, mgr Employees.Manager_ID%TYPE := 145
, sal Employees.Salary%TYPE := 1000
, comm Employees.Commission_PCT%TYPE := 0
, depID Employees.Department_ID%TYPE := 30
) IS
BEGIN
IF valid_deptid(depID) THEN
INSERT INTO Employees (
Employee_ID
, First_Name
, Last_Name
, Email
, Hire_Date
, Job_ID
, Manager_ID
, Salary
, Commission_PCT
, Department_ID
)
VALUES (
Employees_Seq.NEXTVAL -- Employee_ID
, firstName
, lastName
, mail
, TRUNC(SYSDATE) -- Hire_Date
, job
, mgr
, sal
, comm
, depID
);
ELSE
DBMS_OUTPUT.PUT_LINE(
'Error: cannot add an employee to a nonexistent department #' || depID || '.'
);
END IF;
END add_employee;
PROCEDURE get_employee (
id Employees.Employee_ID%TYPE,
jobID OUT Employees.Job_ID%TYPE,
empSalary OUT Employees.Salary%TYPE
) IS
BEGIN
SELECT Job_ID, Salary
INTO jobID, empSalary
FROM Employees
WHERE Employee_ID = id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('get_employee error:');
DBMS_OUTPUT.PUT_LINE(
' could not get Employee information from'
|| ' get_employee(' || id || ', jobID, empSalary).'
);
DBMS_OUTPUT.PUT_LINE(' There is no employee with ID ' || id || '.');
END get_employee;
END emp_pkg;Package Body EMP_PKG compiled
EXECUTE emp_pkg.add_employee('Jane', 'Harris', 'JAHARRIS', depID => 15)Error: cannot add an employee to a nonexistent department #15.
EXECUTE emp_pkg.add_employee('David', 'Smith', 'DASMITH', depID => 80)
SELECT * FROM Employees WHERE Last_Name = 'Smith'
| EMPLOYEE_ID | FIRST_NAME | LAST_NAME | PHONE_NUMBER | HIRE_DATE | JOB_ID | SALARY | COMMISSION_PCT | MANAGER_ID | DEPARTMENT_ID | |
|---|---|---|---|---|---|---|---|---|---|---|
| 210 | David | Smith | DASMITH | (null) | 27.03.19 | SA_REP | 1000 | 0 | 145 | 80 |
| 159 | Lindsey | Smith | LSMITH | 011.44.1345.729268 | 10.03.05 | SA_REP | 8000 | 0.3 | 146 | 80 |
| 171 | William | Smith | WSMITH | 011.44.1343.629268 | 23.02.07 | SA_REP | 7400 | 0.15 | 148 | 80 |
Практическое занятие 13. Работа с пакетами
- Обслуживание пакетов.
- Устойчивое состояние пакета.
PRAGMA SERIALLY_REUSABLE.
Задание 1. Измените пакет emp_pkg, созданный в практическом занятии 12. Необходимо перегрузить процедуру add_employee. Для этого:
- В спецификацию пакета добавьте описание еще одной процедуры
add_employee, принимающей на вход 3 параметра:first_name,last_nameиdepartment_id.
- Сохраните и скомпилируйте внесенные изменения.
- Создайте реализацию новой процедуры в теле пакета.
- Процедура должна формировать e-mail адрес из первой буквы имени и семи первых букв фамилий сотрудника — в верхнем регистре.
- После формирования e-mail, новая версия процедуры
add_employeeдолжна вызывать на исполнение старую версию этой процедуры из этого же пакета, для добавления записи в таблицу. Сформированный e-mail передается при вызове в качестве входного параметра. - Сохраните и скомпилируйте тело пакета.
- Запустите процедуру на исполнение передав значения параметров:
Samuel Joplinи30отдел.
CREATE OR REPLACE PACKAGE emp_pkg IS
-- .....................
PROCEDURE add_employee (
firstName Employees.First_Name%TYPE
, lastName Employees.Last_Name%TYPE
, depID Employees.Department_ID%TYPE
);
-- .....................
END emp_pkg;Package EMP_PKG compiledCREATE OR REPLACE PACKAGE BODY emp_pkg IS
-- .....................
PROCEDURE add_employee (
firstName Employees.First_Name%TYPE
, lastName Employees.Last_Name%TYPE
, depID Employees.Department_ID%TYPE
) IS
email Employees.Email%TYPE := UPPER(SUBSTR(firstName, 1, 1)) || UPPER(SUBSTR(lastName, 1, 7));
BEGIN
add_employee(firstName, lastName, mail => email, depID => depID);
END add_employee;
-- .....................
END emp_pkg;Package Body EMP_PKG compiled
EXECUTE emp_pkg.add_employee('Samuel', 'Joplin', 30)
SELECT * FROM Employees WHERE Last_Name = 'Joplin'| EMPLOYEE_ID | FIRST_NAME | LAST_NAME | PHONE_NUMBER | HIRE_DATE | JOB_ID | SALARY | COMMISSION_PCT | MANAGER_ID | DEPARTMENT_ID | |
|---|---|---|---|---|---|---|---|---|---|---|
| 211 | Samuel | Joplin | SJOPLIN | (null) | 02.04.19 | SA_REP | 1000 | 0 | 145 | 30 |
Задание 2. В пакете emp_pkg создайте 2 функции get_employee.
- В спецификацию пакета добавьте объявление функций:
get_employee, которая принимает на вход параметрp_emp_idтипаEmployees.Employee_ID%TYPEФункция должна возвращать значение типаEmployees%ROWTYPE(запись, соответствующую структуре таблицыEmployees).get_employee, которая принимает на вход параметрp_family_nameтипаEmployees.Last_Name%TYPE. Функция должна возвращать значение типаEmployees%ROWTYPE(запись, соответствующую структуре таблицыEmployees).
- Скомпилируйте спецификацию пакета.
- В тело пакета добавьте реализацию функций.
- Первая
get_employeeдолжна содержать выборку, возвращающую запись о сотруднике с переданным на вход значениемEmployee_ID, - Вторая
get_employeeдолжна отбирать записи о сотрудниках с переданной на вход фамилиейp_family_name.
- Первая
- Скомпилируйте тело пакета.
- Добавьте в пакет процедуру
print_employee.- Процедура принимает на вход параметр типа
Employees%ROWTYPE. - С помощью
DBMS_OUTPUT.PUT_LINEвыводит значение из полейDepartment_ID,Employee_ID,First_Name,Last_Name,Job_ID,Salary.
- Процедура принимает на вход параметр типа
- Скомпилируйте пакет.
- С помощью анонимного блока протестируйте работу пакета. Для этого запустите на исполнение процедуру
emp_pkg.get_employee, передав ей на входemployee_id = 100,family_name = 'Joplin'. А потом с помощью процедурыemp_pkg.print_employeeраспечатайте результат.
CREATE OR REPLACE PACKAGE emp_pkg IS
-- .....................
FUNCTION get_employee (p_emp_id Employees.Employee_ID%TYPE)
RETURN Employees%ROWTYPE;
FUNCTION get_employee (p_family_name Employees.Last_Name%TYPE)
RETURN Employees%ROWTYPE;
PROCEDURE print_employee (emp Employees%ROWTYPE);
END emp_pkg;Package EMP_PKG compiledCREATE OR REPLACE PACKAGE BODY emp_pkg IS
-- .....................
FUNCTION get_employee (p_emp_id Employees.Employee_ID%TYPE)
RETURN Employees%ROWTYPE IS
emp Employees%ROWTYPE;
BEGIN
SELECT *
INTO emp
FROM Employees
WHERE Employee_ID = p_emp_id;
RETURN emp;
END;
FUNCTION get_employee (p_family_name Employees.Last_Name%TYPE)
RETURN Employees%ROWTYPE IS
emp Employees%ROWTYPE;
BEGIN
SELECT *
INTO emp
FROM Employees
WHERE Last_Name = p_family_name;
RETURN emp;
END;
PROCEDURE print_employee (emp Employees%ROWTYPE) IS BEGIN
DBMS_OUTPUT.PUT_LINE(
'Emp#' || emp.Employee_ID || ': '
|| emp.First_Name || ' ' || emp.Last_Name
|| ', depID is ' || emp.Department_ID
|| ', job is ' || emp.Job_ID
|| ', salary is ' || emp.Salary || '.'
);
END;
END emp_pkg;Package Body EMP_PKG compiled
BEGIN
emp_pkg.print_employee(emp_pkg.get_employee(100));
emp_pkg.print_employee(emp_pkg.get_employee('Joplin'));
END;Emp#100: Steven King, depID is 90, job is AD_PRES, salary is 24000.
Emp#211: Samuel Joplin, depID is 30, job is SA_REP, salary is 1000.
Задание 3. Структура отделов организации меняется не часто, поэтому для улучшения производительности пакета emp_pkg добавьте в него public процедуру init_departments и private индексную таблицу valid_departments. Процедура будет один раз, в начале работы с пакетом, заполнять индексную таблицу номерами существующих отделов, для повышения производительности использования функции valid_deptid, которая теперь будет анализировать не актуальную таблицу Departments, а индексную таблицу в теле пакета. Для этого:
- Добавьте в спецификацию пакета объявление процедуры
init_departments, не принимающей на вход никаких параметров. - Добавьте в тело пакета декларирование типа индексной таблицы, саму таблицу
valid_departmentsи описание процедурыinit_departments.- Таблица хранит значения типа
BOOLEANи использует значения из столбцаDepartments.Department_IDв качестве индекса:
TYPE boolean_tab_type IS TABLE OF BOOLEAN INDEX BY BINARY_INTEGER; valid_departments boolean_tab_type; - Таблица хранит значения типа
- Добавьте описание процедуры
init_departmentsв конец тела пакета. Процедура должна заполнить индексную таблицу значениями: используйте значение столбцаDepartment_IDв качестве индекса записи вvalid_departments, а в качестве соответствующего значения записи используйтеTRUE.
init_departments.CREATE OR REPLACE PACKAGE emp_pkg IS
PROCEDURE init_departments;
-- .....................
END emp_pkg;Package EMP_PKG compiled
CREATE OR REPLACE PACKAGE BODY emp_pkg IS
TYPE boolean_tab_type IS TABLE OF BOOLEAN INDEX BY BINARY_INTEGER;
valid_departments boolean_tab_type;
-- .....................
PROCEDURE init_departments IS BEGIN
FOR row IN (SELECT Department_ID FROM Departments) LOOP
valid_departments(row.Department_ID) := TRUE;
END LOOP;
END;
BEGIN
init_departments();
END emp_pkg;
/Package Body EMP_PKG compiled
Задание 4. Измените реализацию функции valid_deptid, таким образом, чтобы она анализировала содержимое индексированной таблицы.
- Поменяйте реализацию функции. Сохраните и скомпилируйте изменения.
- Протестируйте работу пакета. Вызовите на исполнение процедуру
add_employeeс параметрамиname = 'James Bond'иdepartment_id = 15.
- Добавьте в таблицу
Departmentsновый отдел с номером15и названиемSecurity. Подтвердите модификацию с помощью инструкцииCOMMIT. - Ещё раз вызовите на исполнение процедуру
add_employeeс параметрами:name = 'James Bond'иdepartment_id = 15.
- Запустите на исполнение процедуру
emp_pkg.init_departments. Зачем это надо сделать? На что это повлияет? - Ещё раз вызовите на исполнение процедуру
add_employeeс параметрамиname = 'James Bond'иdepartment_id = 15.
- Удалите запись о
James Bondи15отделе из соответствующих таблиц и ещё раз обновите таблицуvalid_departments.
CREATE OR REPLACE PACKAGE emp_pkg IS
PROCEDURE init_departments;
-- .....................
END emp_pkg;Package EMP_PKG compiled
CREATE OR REPLACE PACKAGE BODY emp_pkg IS
TYPE boolean_tab_type IS TABLE OF BOOLEAN INDEX BY BINARY_INTEGER;
valid_departments boolean_tab_type;
FUNCTION valid_deptid (depID Departments.Department_ID%TYPE)
RETURN BOOLEAN IS
BEGIN
IF valid_departments(depID) THEN RETURN TRUE;
ELSE RETURN FALSE; END IF;
EXCEPTION
WHEN OTHERS THEN RETURN FALSE;
END valid_deptid;
-- .....................
PROCEDURE init_departments IS BEGIN
FOR row IN (SELECT Department_ID FROM Departments) LOOP
valid_departments(row.Department_ID) := TRUE;
END LOOP;
END;
BEGIN
init_departments();
END emp_pkg;
/Package Body EMP_PKG compiled
EXECUTE emp_pkg.add_employee('James', 'Bond', 15)Error: cannot add an employee to a nonexistent department #15.Ошибка — у нас нет 15-го отдела.
INSERT INTO Departments (Department_ID, Department_Name)
VALUES (15, 'Security');
COMMIT;1 row inserted.
Commit complete.
EXECUTE emp_pkg.add_employee('James', 'Bond', 15)Error: cannot add an employee to a nonexistent department #15.Всё равно ошибка — мы не обновили таблицу valid_departments.
EXECUTE emp_pkg.init_departments()Мы обновили таблицу, 15-ый отдел теперь виден функции valid_deptid.
EXECUTE emp_pkg.add_employee('James', 'Bond', 15)
SELECT * FROM Employees WHERE Last_Name = 'Bond'| EMPLOYEE_ID | FIRST_NAME | LAST_NAME | PHONE_NUMBER | HIRE_DATE | JOB_ID | SALARY | COMMISSION_PCT | MANAGER_ID | DEPARTMENT_ID | |
|---|---|---|---|---|---|---|---|---|---|---|
| 214 | James | Bond | JBOND | (null) | 02.04.19 | SA_REP | 1000 | 0 | 145 | 15 |
DELETE FROM Employees WHERE Last_Name = 'Bond';
COMMIT;1 row deleted.
Commit complete.DELETE FROM Departments WHERE Department_ID = 15;
COMMIT;1 row deleted.
Commit complete.
EXECUTE emp_pkg.init_departments()
Задание 5. Поменяйте местами описание процедур и функций в пакете так, чтобы их имена шли в алфавитном порядке.
- Перепишите спецификацию пакета так, чтобы все элементы были перечислены в алфавитном порядке. Сохраните и скомпилируйте спецификацию пакета.
- Перепишите тело пакета так, чтобы все элементы были перечислены в алфавитном порядке. Сохраните и скомпилируйте тело пакета. Зафиксируйте результат.
- Сделайте все необходимое, чтобы исправить ошибки компиляции. С чем они связаны? Выполните необходимые действия (подсказка:
forward declaration).
CREATE OR REPLACE PACKAGE emp_pkg IS
PROCEDURE add_employee (
firstName Employees.First_Name%TYPE
, lastName Employees.Last_Name%TYPE
, mail Employees.Email%TYPE
, job Employees.Job_ID%TYPE := 'SA_REP'
, mgr Employees.Manager_ID%TYPE := 145
, sal Employees.Salary%TYPE := 1000
, comm Employees.Commission_PCT%TYPE := 0
, depID Employees.Department_ID%TYPE := 30
);
PROCEDURE add_employee (
firstName Employees.First_Name%TYPE
, lastName Employees.Last_Name%TYPE
, depID Employees.Department_ID%TYPE
);
PROCEDURE get_employee (
id Employees.Employee_ID%TYPE
, jobID OUT Employees.Job_ID%TYPE
, empSalary OUT Employees.Salary%TYPE
);
FUNCTION get_employee (p_emp_id Employees.Employee_ID%TYPE)
RETURN Employees%ROWTYPE;
FUNCTION get_employee (p_family_name Employees.Last_Name%TYPE)
RETURN Employees%ROWTYPE;
PROCEDURE init_departments;
PROCEDURE print_employee (emp Employees%ROWTYPE);
END emp_pkg;Package EMP_PKG compiledCREATE OR REPLACE PACKAGE BODY emp_pkg IS
TYPE boolean_tab_type IS TABLE OF BOOLEAN INDEX BY BINARY_INTEGER;
valid_departments boolean_tab_type;
FUNCTION valid_deptid (depID Departments.Department_ID%TYPE) RETURN BOOLEAN;
/*
--- PUBLIC ---
*/
PROCEDURE add_employee (
firstName Employees.First_Name%TYPE
, lastName Employees.Last_Name%TYPE
, mail Employees.Email%TYPE
, job Employees.Job_ID%TYPE := 'SA_REP'
, mgr Employees.Manager_ID%TYPE := 145
, sal Employees.Salary%TYPE := 1000
, comm Employees.Commission_PCT%TYPE := 0
, depID Employees.Department_ID%TYPE := 30
) IS
BEGIN
IF valid_deptid(depID) THEN
INSERT INTO Employees (
Employee_ID
, First_Name
, Last_Name
, Email
, Hire_Date
, Job_ID
, Manager_ID
, Salary
, Commission_PCT
, Department_ID
)
VALUES (
Employees_Seq.NEXTVAL -- Employee_ID
, firstName
, lastName
, mail
, TRUNC(SYSDATE) -- Hire_Date
, job
, mgr
, sal
, comm
, depID
);
ELSE
DBMS_OUTPUT.PUT_LINE(
'Error: cannot add an employee to a nonexistent department #' || depID || '.'
);
END IF;
END add_employee;
PROCEDURE add_employee (
firstName Employees.First_Name%TYPE
, lastName Employees.Last_Name%TYPE
, depID Employees.Department_ID%TYPE
) IS
email Employees.Email%TYPE := UPPER(SUBSTR(firstName, 1, 1)) || UPPER(SUBSTR(lastName, 1, 7));
BEGIN
add_employee(firstName, lastName, mail => email, depID => depID);
END add_employee;
PROCEDURE get_employee (
id Employees.Employee_ID%TYPE,
jobID OUT Employees.Job_ID%TYPE,
empSalary OUT Employees.Salary%TYPE
) IS
BEGIN
SELECT Job_ID, Salary
INTO jobID, empSalary
FROM Employees
WHERE Employee_ID = id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
DBMS_OUTPUT.PUT_LINE('get_employee error:');
DBMS_OUTPUT.PUT_LINE(
' could not get Employee information from'
|| ' get_employee(' || id || ', jobID, empSalary).'
);
DBMS_OUTPUT.PUT_LINE(' There is no employee with ID ' || id || '.');
END get_employee;
FUNCTION get_employee (p_emp_id Employees.Employee_ID%TYPE)
RETURN Employees%ROWTYPE IS
emp Employees%ROWTYPE;
BEGIN
SELECT *
INTO emp
FROM Employees
WHERE Employee_ID = p_emp_id;
RETURN emp;
END;
FUNCTION get_employee (p_family_name Employees.Last_Name%TYPE)
RETURN Employees%ROWTYPE IS
emp Employees%ROWTYPE;
BEGIN
SELECT *
INTO emp
FROM Employees
WHERE Last_Name = p_family_name;
RETURN emp;
END;
PROCEDURE init_departments IS BEGIN
DBMS_OUTPUT.PUT_LINE(someVar || ' -- some var');
FOR row IN (SELECT Department_ID FROM Departments) LOOP
valid_departments(row.Department_ID) := TRUE;
END LOOP;
END;
PROCEDURE print_employee (emp Employees%ROWTYPE) IS BEGIN
DBMS_OUTPUT.PUT_LINE(
'Emp#' || emp.Employee_ID || ': '
|| emp.First_Name || ' ' || emp.Last_Name
|| ', depID is ' || emp.Department_ID
|| ', job is ' || emp.Job_ID
|| ', salary is ' || emp.Salary || '.'
);
END;
/*
--- PRIVATE ---
*/
FUNCTION valid_deptid (depID Departments.Department_ID%TYPE)
RETURN BOOLEAN IS
BEGIN
IF valid_departments(depID) THEN RETURN TRUE;
ELSE RETURN FALSE; END IF;
EXCEPTION
WHEN OTHERS THEN RETURN FALSE;
END valid_deptid;
BEGIN
init_departments();
END emp_pkg;
/Package Body EMP_PKG compiled
Если мы закомментируем строчку (с forward declaration):
FUNCTION valid_deptid (depID Departments.Department_ID%TYPE) RETURN BOOLEAN;то получим следующую ошибку:
