first commit

This commit is contained in:
2025-12-15 22:27:56 +03:00
commit d31e31332f
231 changed files with 242246 additions and 0 deletions

571
labs/lab4/report.typ Normal file
View File

@@ -0,0 +1,571 @@
#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, а также убедился, что изменения структуры таблиц, от которых зависят представления, могут быть заблокированы. Были созданы две скалярные функции одна для получения полного имени сотрудника, другая для возврата зарплаты в разных режимах.
Также была реализована собственная процедура с ветвлением, выполняющая разные действия в зависимости от режима.