diff --git a/lab_13.sql b/lab_13.sql new file mode 100644 index 0000000..5d903ff --- /dev/null +++ b/lab_13.sql @@ -0,0 +1,332 @@ +-- Лабораторная 13. Создание триггеров + +-- 1. Реализуйте следующее решение: +-- a. Создайте триггерную функцию check_salary, которая вызывает +-- исключение, если новая зарплата сотрудника больше старой на 100%. +create or replace function schema1.check_salary() +returns trigger +language plpgsql +as $$ +begin + if new.salary is distinct from old.salary then + if new.salary > old.salary * 2 then + raise exception + 'зарплата увеличена более чем на 100%% (%s, %s, employee_id=%s)', + old.salary, new.salary, old.employee_id; + end if; + end if; + + return new; +end; +$$; + +-- b. Создайте триггер BEFORE UPDATE - before_update_salary, который +-- вызывает функцию check_salary перед обновлением значения в столбце salary для каждой записи. +create trigger before_update_salary +before update of salary on schema1.employees +for each row +execute function schema1.check_salary(); + +-- c. Обновите зарплату сотрудника с идентификатором 1 и убедитесь, +-- что триггер сработал и вернул соответствующее сообщение +update schema1.employees +set salary = salary * 3 +where employee_id = 1; +-- сработал триггер и изменение не применилось, так как вызвался raise exception + +-- d. Переименуйте before_update_salary триггер в salary_before_update +alter trigger before_update_salary +on schema1.employees +rename to salary_before_update; + +-- e. Отключите триггер salary_before_update. Обновите зарплату сотрудника +-- с идентификатором 2 и убедитесь, что триггер не сработал. +alter table schema1.employees +disable trigger salary_before_update; + +update schema1.employees +set salary = salary * 3 +where employee_id = 2; +-- изменилась + +-- f. Измените имя функции check_salary на validate_salary. +alter function schema1.check_salary() +rename to validate_salary; + +-- g. Измените «привязку» триггера к триггерной функции validate_salary +drop trigger salary_before_update on schema1.employees; + +create trigger salary_before_update +before update of salary on schema1.employees +for each row +execute function schema1.validate_salary(); + +-- h. Выполните проверку +update schema1.employees +set salary = salary * 3 +where employee_id = 1; +-- триггер сработал + +-- 2. Добавьте в таблицу employees 2 новых столбца: +-- a. login – для сохранения учетной записи сотрудника +-- b. password– для сохранения пароля сотрудника + +alter table schema1.employees +add column login character varying, +add column password character varying; + +-- 3. Создайте триггер, который будет срабатывать при добавлении или изменении +-- записей о сотрудниках: +-- a. Login и password не должны быть одинаковыми, при этом их длина должна быть +-- больше или равна 8 символов. Поля не должны содержать NULL значения. +-- b. В случае нарушения данных условий должно генерироваться пользовательское +-- исключение, предоставляющее информацию о нарушении + +create function schema1.validate_employee() +returns trigger +language plpgsql +as $$ +begin + if new.login is null or new.password is null then + raise exception + 'поля login и password не должны быть null'; + end if; + + if length(new.login) < 8 or length(new.password) < 8 then + raise exception + 'длина login и password должна быть не менее 8 символов'; + end if; + + if new.login = new.password then + raise exception + 'login и password не должны совпадать'; + end if; + + return new; +end; +$$; +create trigger validate_employee_data +before insert or update of login, password +on schema1.employees +for each row +execute function schema1.validate_employee(); + +-- c. Протестируйте ваш триггер +update schema1.employees +set login = null +where employee_id = 1; + +update schema1.employees +set login = '123', + password = '125678932' +where employee_id = 1; + +update schema1.employees +set login = '12345678', + password = '12345678' +where employee_id = 1; + +update schema1.employees +set login = 'user_login', + password = 'secure_pwd' +where employee_id = 1; + +-- все работает + +-- d. Удалите созданный триггер +drop trigger validate_employee_data on schema1.employees; + +-- 4. Необходимо реализовать процедурную поддержку процесса карьерных +-- перемещений сотрудников - таблица job_history в схеме schema1 +-- a. Таблица employees должна содержать только актуальную информацию о активных сотрудниках +-- b. Таблица job_history должна содержать информацию о всех «изменениях» сотрудника: прием, +-- увольнение, изменение должности, изменение отдела, изменение заработной платы. +-- c. Тип изменения должен сохранятся в столбце type_operation в таблице job_history + +ALTER TABLE schema1.job_history +ALTER COLUMN end_date DROP NOT NULL; +ALTER TABLE schema1.job_history +ADD COLUMN location_id numeric(4,0), +ADD COLUMN type_operation text; + + +create or replace function schema1.employee_changes() +returns trigger +language plpgsql +as $$ +declare + v_employee_id numeric(6,0); + v_job_id varchar(10); + v_department_id numeric(4,0); + v_salary numeric(10,2); + v_location_id numeric(4,0); + v_start_date date; + v_end_date date; + v_op text; +begin + if lower(tg_op) = 'insert' then + v_employee_id := new.employee_id; + v_job_id := new.job_id; + v_department_id := new.department_id; + v_salary := new.salary; + v_start_date := new.hire_date; + v_end_date := null; + v_op := 'hire'; + + elsif lower(tg_op) = 'update' then + if new.job_id is distinct from old.job_id then + v_op := 'job_change'; + elsif new.department_id is distinct from old.department_id then + v_op := 'dept_change'; + elsif new.salary is distinct from old.salary then + v_op := 'salary_change'; + else + return new; + end if; + + v_employee_id := new.employee_id; + v_job_id := new.job_id; + v_department_id := new.department_id; + v_salary := new.salary; + v_start_date := current_date; + v_end_date := null; + + update schema1.job_history + set end_date = current_date + where employee_id = old.employee_id + and end_date is null; + + elsif lower(tg_op) = 'delete' then + v_employee_id := old.employee_id; + v_job_id := old.job_id; + v_department_id := old.department_id; + v_salary := old.salary; + v_start_date := current_date; + v_end_date := current_date; + v_op := 'fire'; + + update schema1.job_history + set end_date = current_date + where employee_id = old.employee_id + and end_date is null; + + else + return null; + end if; + + select d.location_id + into v_location_id + from schema1.departments d + where d.department_id = v_department_id; + + insert into schema1.job_history ( + employee_id, start_date, end_date, + job_id, department_id, salary, + location_id, type_operation + ) + values ( + v_employee_id, v_start_date, v_end_date, + v_job_id, v_department_id, v_salary, + v_location_id, v_op + ); + + if lower(tg_op) = 'delete' then + return old; + end if; + + return new; +end; +$$; + +drop trigger if exists employee_history on schema1.employees; + + +create trigger employee_history +after insert or update or delete +on schema1.employees +for each row +execute function schema1.employee_changes(); + +select count(*) as before_cnt +from schema1.job_history +where employee_id = 1; + +-- проверили кол-во строк в истории + +update schema1.employees +set salary = salary + 1 +where employee_id = 1; + +-- сделали апдейт + +select count(*) as after_cnt +from schema1.job_history +where employee_id = 1; + +-- должно стать на 1 запись в истории больше + +select * +from schema1.job_history +where employee_id = 1; + +-- проверили запись + +-- все работает + + +-- 5. Необходимо реализовать процедурную поддержку для проверки номеров телефона +-- сотрудников на соответствие заданному шаблону. Проверка должна осуществляться как при +-- вставке новых записей о сотрудниках, так и при изменении существующих. +create function schema1.validate_phone_array() +returns trigger +language plpgsql +as $$ +declare + ph phone; +begin + if new.phone_number is null or cardinality(new.phone_number) = 0 then + raise exception 'номер не должен быть null или пустым'; + end if; + + foreach ph in array new.phone_number loop + if ph is null then + raise exception 'номер не должен содержать null значения'; + end if; + end loop; + + if (select count(*) from unnest(new.phone_number) x) <> + (select count(distinct x) from unnest(new.phone_number) x) then + raise exception 'номер не должен содержать повторяющиеся номера'; + end if; + + return new; +end; +$$; + +create trigger validate_phone_number +before insert or update of phone_number +on schema1.employees +for each row +execute function schema1.validate_phone_array(); + + +update schema1.employees +set phone_number = '{}'::phone[] +where employee_id = 1; + +update schema1.employees +set phone_number = array['8(900)000-4432','8(900)000-4432']::phone[] +where employee_id = 1; + +update schema1.employees +set phone_number = array[null]::phone[] +where employee_id = 1; + +update schema1.employees +set phone_number = array['8(950)111-2357','8(988)467-7000']::phone[] +where employee_id = 1; + +select * from schema1.employees +where employee_id = 1; + +-- все работает!