first commit
This commit is contained in:
571
labs/lab4/report.typ
Normal file
571
labs/lab4/report.typ
Normal 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, а также убедился, что изменения структуры таблиц, от которых зависят представления, могут быть заблокированы. Были созданы две скалярные функции — одна для получения полного имени сотрудника, другая для возврата зарплаты в разных режимах.
|
||||
|
||||
Также была реализована собственная процедура с ветвлением, выполняющая разные действия в зависимости от режима.
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user