Files
db/labs/lab4/report.typ
2025-12-15 22:27:56 +03:00

572 lines
20 KiB
Typst
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#show link: underline
#set text(size: 1.3em)
#set page(
header: context {
if counter(page).get().first() == 1 [
#align(center)[
Санкт-Петербургский национальный исследовательский университет информационных технологий, механики и оптики
]
]
},
footer: context {
if counter(page).get().first() == 1 [
#align(center)[Санкт-Петербург \ 2025]
] else [
#align(center)[#counter(page).display("1")]
]
}
)
#show raw.where(block: false): box.with(
fill: luma(240),
inset: (x: 3pt, y: 0pt),
outset: (y: 3pt),
radius: 2pt,
)
#show raw.where(block: true): block.with(
fill: luma(240),
inset: 10pt,
radius: 4pt,
)
// title
\
\
\
#align(center)[Факультет инфокоммуникационных технологий]
#align(center)[Направление подготовки 11.03.02]
\
\
#align(center)[Практическая работа №4]
//#align(center)[Установка и первоначальная настройка субд postgresql.]
\
\
\
\ //#align(center)[Вариант 19]
\
\
\
\
\
\
\
#align(right)[Выполнил:]
#align(right)[Дощенников Никита Андреевич]
#align(right)[Группа: К3221]
#align(right)[Проверила:]
#align(right)[Татьяна Евгеньевна Войтюк]
#pagebreak()
=== Цель работы
Освоить создание и использование обычных и материализованных представлений, пользовательских функций и хранимых процедур.
=== Задачи, решаемые при выполнении работы
Создать обычные и материализованные представления, протестировать их работу, изучить влияние изменений в базовых таблицах, разработать пользовательские функции, создать и вызвать хранимые процедуры, проверить корректность их выполнения.
=== Исходные данные
СУБД PostgreSQL, база данных `appdb`, схема "EmployeesDepartments", таблица "EMPLOYEES" с полями сотрудника.
=== Выполнение работы
// Краткое описание процесса выполнения всех задач по шагам (при наличии нескольких шагов) со скриншотами.
==== Задание 1. Создание представления с помощью графического интерфейса
Я открыл окно создание представления в БД `appdb` в схеме "EmployeesDepartments". (@img1)
#align(center)[
#figure(
image("assets/1.png"),
supplement: [Рис.],
caption: [Создание представления.]
) <img1>
]
Ввел наименование представления `v_emp_active` на вкладке General. (@img2)
#align(center)[
#figure(
image("assets/2.png"),
supplement: [Рис.],
caption: [Задание наименования представления.]
) <img2>
]
На вкладке Code я ввел его определение. (@img3)
```sql
SELECT "EMPLOYEE_ID",
"FIRST_NAME",
"LAST_NAME",
"EMAIL",
"JOB_ID",
"SALARY",
"DEPARTMENT_ID"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "SALARY" > 5000;
```
#align(center)[
#figure(
image("assets/3.png"),
supplement: [Рис.],
caption: [Создание тела представления `v_emp_active`.]
) <img3>
]
Затем я протестировал созданное представление командой ниже. (@img4)
```sql
SELECT * FROM "EmployeesDepartments".v_emp_active
```
#align(center)[
#figure(
image("assets/4.png"),
supplement: [Рис.],
caption: [Тестирование представления `v_emp_active`.]
) <img4>
]
==== Задание 2. Создание представления на основе представления и изменение базовых таблиц представлений.
Аналогичным образом в схеме "EmployeesDepartments" я создал еще одно представление, которое имеет название `V_EMP_ACTIVE_INFO` и предназначеное для отбора сотрудников из отдела 80 с заработной платой выше 5000. Для этого я определил DDL оператор `CREATE` для создания нового представления. (@img5)
```sql
CREATE OR REPLACE VIEW "EmployeesDepartments"."V_EMP_ACTIVE_INFO" AS
SELECT "EMPLOYEE_ID",
"FIRST_NAME" || ' ' || "LAST_NAME" AS FULL_NAME,
"EMAIL",
"JOB_ID",
"SALARY",
"DEPARTMENT_ID"
FROM "EmployeesDepartments".v_emp_active
WHERE "DEPARTMENT_ID" = 80;
```
#align(center)[
#figure(
image("assets/5.png"),
supplement: [Рис.],
caption: [Создание представления `V_EMP_ACTIVE_INFO`.]
) <img5>
]
Затем, я протестировал созданное представление (@img6)
```sql
SELECT * FROM "EmployeesDepartments"."V_EMP_ACTIVE_INFO"
```
#align(center)[
#figure(
image("assets/6.png"),
supplement: [Рис.],
caption: [Тестирование созданного представления.]
) <img6>
]
После этого я провел дальнейшее тестирование представления `V_EMP_ACTIVE_INFO`. Для этого я добавил одну запись непосредственно в таблицу "EMPLOYEES", а вторую запись добавил через представление `v_emp_active` и проверил, что представление `V_EMP_ACTIVE_INFO` отображает корректные данные. (@img7)
```sql
INSERT INTO "EmployeesDepartments"."EMPLOYEES"
("FIRST_NAME", "LAST_NAME", "EMAIL", "JOB_ID", "SALARY", "DEPARTMENT_ID")
VALUES('Nik', 'Nikov', 'Ni_Nik@itmo.ru', 'SA_REP', 6000, 80 );
INSERT INTO "EmployeesDepartments".v_emp_active
("FIRST_NAME", "LAST_NAME", "EMAIL", "JOB_ID", "SALARY", "DEPARTMENT_ID")
VALUES('Sem', 'Semov', 'Se_Sem@itmo.ru', 'AD_VP', 22000, 80 );
SELECT * FROM "EmployeesDepartments"."V_EMP_ACTIVE_INFO"
```
#align(center)[
#figure(
image("assets/7.png"),
supplement: [Рис.],
caption: [Тестирование представления `V_EMP_ACTIVE_INFO`.]
) <img7>
]
Затем, я попробовал внести изменения в таблицу "EMPLOYEES" с помощью кода ниже (@img8)
```sql
ALTER TABLE "EmployeesDepartments"."EMPLOYEES"
ALTER COLUMN "FIRST_NAME" TYPE varchar(100);
```
#align(center)[
#figure(
image("assets/8.png"),
supplement: [Рис.],
caption: [Попытка изменения таблицы "EMPLOYEES".]
) <img8>
]
Я получил ошибку (@img8), которая говорит о том, что мы не можем менять тип столбца, используемого представлением.
==== Задание 3. Создание материализованного представления
Я пересоздал существующее представление с использованием директивы `MATERIALIZED`. (@img9)
```sql
DROP VIEW IF EXISTS "EmployeesDepartments".v_emp_active CASCADE;
CREATE MATERIALIZED VIEW "EmployeesDepartments".v_emp_active AS
SELECT "EMPLOYEE_ID",
"FIRST_NAME",
"LAST_NAME",
"EMAIL",
"JOB_ID",
"SALARY",
"DEPARTMENT_ID"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "SALARY" > 5000;
```
#align(center)[
#figure(
image("assets/9.png"),
supplement: [Рис.],
caption: [Создание материализованного представления.]
) <img9>
]
Затем я проверил новое представление (@img10):
```sql
SELECT * FROM "EmployeesDepartments".v_emp_active WHERE LAST_NAME = "Ivanov";
```
#align(center)[
#figure(
image("assets/10.png"),
supplement: [Рис.],
caption: [Проверка представления `v_emp_active`.]
) <img10>
]
Затем, я обновил имя сотрудника с фамилией "Petrov" в таблице "Employees", установив ему имя "Vlad". (@img11)
```sql
UPDATE "EmployeesDepartments"."EMPLOYEES"
SET "FIRST_NAME" = 'Vlad'
WHERE "LAST_NAME" = 'Petrov';
```
#align(center)[
#figure(
image("assets/11.png"),
supplement: [Рис.],
caption: [Обновление имени сотрудника.]
) <img11>
]
Проверим, что данные обновились в таблице "EMPLOYEES". (@img12)
```sql
SELECT * FROM "EmployeesDepartments"."EMPLOYEES" WHERE "LAST_NAME" = 'Petrov';
```
#align(center)[
#figure(
image("assets/12.png"),
supplement: [Рис.],
caption: [Проверка обновления данных в таблице.]
) <img12>
]
Чтобы получить актуальную информацию из материализованного представления, я обновил его с помощью команды `REFRESH MATERIALIZED VIEW`. (@img13)
```sql
REFRESH MATERIALIZED VIEW "EmployeesDepartments".v_emp_active;
SELECT * FROM "EmployeesDepartments".v_emp_active
WHERE "LAST_NAME" = 'Petrov';
```
#align(center)[
#figure(
image("assets/13.png"),
supplement: [Рис.],
caption: [Обновление представления.]
) <img13>
]
Обычное представление не хранит данные, а каждый раз вычисляет результат заново, поэтому оно всегда показывает актуальную информацию. Материализованное представление хранит готовые данные, работает быстрее, но может содержать устаревшие значения и требует обновления. Обычное представление используют, когда важна актуальность данных, а материализованное когда запрос тяжёлый, данные меняются редко и нужна высокая скорость чтения. (@img14)
==== Задание 4. Создание скалярной пользовательской функции
В окне Query Tool я создал функцию, которая по идентификатору сотрудника, возвращает его полное имя, т.е. имя, соединенное с фамилией, через пробел. Если сотрудник не найден, то функция должна вернуть значение `NULL`.
```sql
CREATE OR REPLACE FUNCTION
"EmployeesDepartments".get_employee_full_name(p_emp_id INT)
RETURNS TEXT AS
$$
DECLARE
v_full_name TEXT;
BEGIN
SELECT "FIRST_NAME" || ' ' || "LAST_NAME"
INTO v_full_name
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = p_emp_id;
RETURN v_full_name;
END;
$$ LANGUAGE plpsql;
```
#align(center)[
#figure(
image("assets/14.png"),
supplement: [Рис.],
caption: [Создание скалярной функции.]
) <img14>
]
Затем я протестировал работоспособность скаларной функции, передав в нее известный мне идентификатор сотрудника. (@img15)
#align(center)[
#figure(
image("assets/15.png"),
supplement: [Рис.],
caption: [Тестирование скалярной функции.]
) <img15>
]
==== Задание 5. Создание собственной скалярной функции
Я создал собственную функцию (@img16)
```sql
CREATE OR REPLACE FUNCTION "EmployeesDepartments".get_salary_by_mode(p_emp_id INT, p_mode TEXT)
RETURNS NUMERIC AS
$$
DECLARE
v_salary NUMERIC;
BEGIN
SELECT "SALARY" INTO v_salary
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = p_emp_id;
IF v_salary IS NULL THEN
RETURN NULL;
END IF;
IF upper(p_mode) = 'ROUND' THEN
RETURN round(v_salary);
ELSIF upper(p_mode) = 'EXACT' THEN
RETURN v_salary;
ELSE
RETURN v_salary;
END IF;
END;
$$ LANGUAGE plpgsql;
```
#align(center)[
#figure(
image("assets/16.png"),
supplement: [Рис.],
caption: [Создание функции.]
) <img16>
]
Точный режим. Находим сотрудника по имени и запросим точную зарплату. (@img17)
#align(center)[
#figure(
image("assets/17.png"),
supplement: [Рис.],
caption: [Демонстрация точного режима.]
) <img17>
]
Округленный режим. Округляет зарплату до целого числа. (@img18)
#align(center)[
#figure(
image("assets/18.png"),
supplement: [Рис.],
caption: [Демонстрация округленного режима.]
) <img18>
]
==== Задание 6. Создание хранимой процедуры
Я создал хранимую процедуру, предназначенную для увеличения заработной платы выбранного сотрудника на указанный процент. (@img19)
```sql
CREATE OR REPLACE PROCEDURE "EmployeesDepartments".raise_salary(
p_employee_id INTEGER,
p_percent NUMERIC
)
LANGUAGE plpgsql
AS $$
BEGIN
-- Увеличиваем зарплату сотрудника
UPDATE "EmployeesDepartments"."EMPLOYEES"
SET "SALARY" = "SALARY" * (1 + p_percent / 100)
WHERE "EMPLOYEE_ID" = p_employee_id;
END;
$$;
```
#align(center)[
#figure(
image("assets/19.png"),
supplement: [Рис.],
caption: [Создание процедуры `raise_salary`.]
) <img19>
]
Затем я проверил работу созданной хранимой процедуры. Для этого я проверил зарплату сотрудника с идентификатором `100`. (@img20)
```sql
SELECT "SALARY" FROM "EmployeesDepartment"."EMPLOYEES" WHERE "EMPLOYEE_ID" = 100;
```
#align(center)[
#figure(
image("assets/20.png"),
supplement: [Рис.],
caption: [Зарплата "номера 100".]
) <img20>
]
Затем я вызвал процедуру, которая повысила заработную плату сотруднику на 10%. (@img21)
```sql
CALL "EmployeesDepartments".raise_salary(100, 10);
```
#align(center)[
#figure(
image("assets/21.png"),
supplement: [Рис.],
caption: [Вызов процедуры `raise_salary`.]
) <img21>
]
В процедуру не обязательно передавать параметры в той последовательности, в которой они были объявлены. К параметрам процедуры можно обращаться по имени. (@img22)
```sql
SELECT * FROM "EmployeesDepartments"."EMPLOYEES" WHERE "EMPLOYEE_ID" = 101;
CALL "EmployeesDepartments".raise_salary(p_percent:=10, p_employee_id:=101);
SELECT * FROM "EmployeesDepartments"."EMPLOYEES" WHERE "EMPLOYEE_ID" = 101;
```
#align(center)[
#figure(
image("assets/22.png"),
supplement: [Рис.],
caption: [Обращение к параметрам процедуры `raise_salary` по имени]
) <img22>
]
==== Задание 7. Создание собственной хранимой процедуры
Я создал процедуру. (@img23)
```sql
CREATE OR REPLACE PROCEDURE "EmployeesDepartments".manage_employee(p_emp_id INT, p_mode TEXT)
LANGUAGE plpgsql AS
$$
BEGIN
IF NOT EXISTS(
SELECT 1
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = p_emp_id
) THEN
RETURN;
END IF;
IF upper(p_mode) = 'INCREASE' THEN
UPDATE "EmployeesDepartments"."EMPLOYEES"
SET "SALARY" = round("SALARY" * 1.10)
WHERE "EMPLOYEE_ID" = p_emp_id;
ELSIF upper(p_mode) = 'BONUS' THEN
UPDATE "EmployeesDepartments"."EMPLOYEES"
SET "SALARY" = "SALARY" + 1000
WHERE "EMPLOYEE_ID" = p_emp_id;
ELSE
RETURN;
END IF;
END;
$$;
```
#align(center)[
#figure(
image("assets/23.png"),
supplement: [Рис.],
caption: [Создание процедуры.]
) <img23>
]
Режим `INCREASE`. Увеличение на 10% (@img24)
```sql
SELECT "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = 100;
CALL "EmployeesDepartments".manage_employee(100, 'INCREASE');
SELECT "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = 100;
```
#align(center)[
#figure(
image("assets/24.png"),
supplement: [Рис.],
caption: [Режим `INCREASE`.]
) <img24>
]
Режим `BONUS`. Прибавление 1000 (@img25)
```sql
SELECT "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = 100;
CALL "EmployeesDepartments".manage_employee(100, 'BONUS');
SELECT "EMPLOYEE_ID", "FIRST_NAME", "LAST_NAME", "SALARY"
FROM "EmployeesDepartments"."EMPLOYEES"
WHERE "EMPLOYEE_ID" = 100;
```
#align(center)[
#figure(
image("assets/25.png"),
supplement: [Рис.],
caption: [Режим `BONUS`.]
) <img25>
]
=== Выводы и анализ результатов работы
// Обобщение результатов выполнения всех задач работы: что должны были достичь, что фактически достигли и каким образом, с какими трудностями столкнулись, какие проблемы на каких этапах выполнения возникли и как именно были решены.
В ходе выполнения работы я последовательно создал обычные и материализованные представления, пользовательские функции и хранимые процедуры, а также проверил их работу на практических примерах. Была получена ясная разница между видами представлений: обычное всегда возвращает актуальные данные, так как вычисляет запрос при каждом обращении, а материализованное хранит результат и требует обновления, но работает быстрее. Это проявилось в том, что изменения в таблице "Employees" не отражались в материализованном представлении до выполнения команды обновления.
В процессе работы я освоил создание представлений как через интерфейс pgadmin, так и через sql, а также убедился, что изменения структуры таблиц, от которых зависят представления, могут быть заблокированы. Были созданы две скалярные функции одна для получения полного имени сотрудника, другая для возврата зарплаты в разных режимах.
Также была реализована собственная процедура с ветвлением, выполняющая разные действия в зависимости от режима.