Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
293 changes: 293 additions & 0 deletions lab_12.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,293 @@
--Лабораторная работа 12. Создание процедур и функций на языке plpgsql. Обработка исключений

-- 1. Создайте функцию, которая будет принимать массив символьных значений и шаблон
-- (например, шаблон телефона). Функция должна возвращать False если хотя бы значение одного
-- элемента массива не соответствует шаблону. Иначе функция должна вернуть – True.
-- Функция должна работать на произвольном количестве элементов массива с произвольным шаблоном.

create function schema2.check_array(
values_arr text[],
pattern text
)
returns boolean
language plpgsql
as $$
declare
v text;
begin
if values_arr is null or pattern is null then
return false;
end if;

foreach v in array values_arr loop
if v !~ pattern then
return false;
end if;
end loop;

return true;
end;
$$;

select schema2.check_array(
array['123','456'], '^[0-9]{3}$'
)::text as result;

-- 2. Создайте процедуру. Процедура должна получать идентификатор
-- сотрудника и его телефон.

create domain public.phone as text
check (
value ~ '^8\([0-9]{3}\)[0-9]{3}-[0-9]{4}$'
);
--(не смог найти созданный ранее)

create or replace procedure schema2.add_employee(
p_employee_id numeric,
p_phone text
)
language plpgsql
as $$
declare
phones text[];
digits text;
begin
if p_phone is null or length(trim(p_phone)) = 0 then
raise exception 'поле "телефон" пустое';
end if;

select e.phone_number
into phones
from schema1.employees e
where e.employee_id = p_employee_id;

if not found then
raise exception 'сотрудник с id % не найден', p_employee_id;
end if;

phones := coalesce(phones, array[]::text[]);

digits := regexp_replace(p_phone, '\D', '', 'g');

if length(digits) = 11 and left(digits, 1) = '8' then
digits := '7' || substr(digits, 2);
end if;

if digits !~ '^7[0-9]{10}$' then
raise exception 'неверный формат телефона: %', p_phone;
end if;

if digits = any(phones) then
raise info 'телефон % уже существует у сотрудника %', digits, p_employee_id;
return;
end if;

update schema1.employees
set phone_number = array_append(phones, digits)
where employee_id = p_employee_id;

raise info 'номер % добавлен сотруднику % (из "%")', digits, p_employee_id, p_phone;
end;
$$;


call schema2.add_employee(13, '+7-999-123-45-67');
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. Если попробовать добавить нормальный номер телефона (который не плохой), то появляется такая ошибка:
padii_plyugin2=> call schema2.add_employee(1, '8(800)555-3535');
ERROR:  value for domain phone violates check constraint "phone_check"
CONTEXT:  PL/pgSQL function schema1.validate_phone_array() line 9 at FOREACH over array
SQL statement "update schema1.employees
    set phone_number = array_append(phones, digits)
    where employee_id = p_employee_id"
PL/pgSQL function schema2.add_employee(numeric,text) line 36 at SQL statement

Это конфликт с лабой 11 или 13 вроде бы.


select employee_id, phone_number
from schema1.employees
where employee_id = 13;
-- номер добавлен успешно

call schema2.add_employee(13, '8(999)123-4567');
-- номер существует

call schema2.add_employee(1000, '8(999)123-4567');
-- такого сотрудника нет



-- 3. Создайте процедуру для добавления нового департамента.
create procedure schema2.add_department(
department_id numeric,
department_name text,
manager_id numeric,
location_id numeric default null
)
language plpgsql
as $$
begin
if department_id is null then
raise exception 'не заполнено обязательное поле department_id';
end if;

if department_name is null or length(trim(department_name)) = 0 then
raise exception 'не заполнено обязательное поле department_name';
end if;

if manager_id is null then
raise exception 'не заполнено обязательное поле manager_id';
end if;

begin
insert into schema1.departments(department_id, department_name, manager_id, location_id)
values (department_id, department_name, manager_id, location_id);

raise info 'департамент "%" (id=%) успешно добавлен', department_name, department_id;

exception
when unique_violation then
raise exception 'department_id или другое уникальное поле уже существует';
when foreign_key_violation then
raise exception 'нарушение внешнего ключа (manager_id/location_id)';
when not_null_violation then
raise exception 'не заполнено обязательное поле (NOT NULL)';
when check_violation then
raise exception 'значение не прошло проверку (CHECK)';
when others then
raise exception 'ошибка при добавлении департамента: %', sqlerrm;
end;
end;
$$;

call schema2.add_department(5001, 'Dept_1', 100);
-- добавлено

call schema2.add_department(null, 'Dept_2', 100);
-- не заполнено обязательное поле

call schema2.add_department(9300, 'Dept_3', 9999999, null);
-- ошибка при добавлении департамента



-- 4
create procedure schema2.add_address(
p_full_address text
)
language plpgsql
as $$
declare
parts text[];
v_location_id integer;
v_street text;
v_postal text;
v_city text;
v_country_id integer;
begin
if p_full_address is null or length(trim(p_full_address)) = 0 then
raise exception 'строка адреса пустая';
end if;

parts := string_to_array(p_full_address, ';');

if array_length(parts, 1) <> 4 then
raise exception 'формат "street_address; postal_code; city; country_id"';
end if;

v_street := trim(parts[1]);
v_postal := nullif(trim(parts[2]), '');
v_city := trim(parts[3]);

begin
v_country_id := trim(parts[4])::integer;
exception
when invalid_text_representation then
raise exception 'country_id должен быть числом';
end;

if v_street is null or v_city is null
or length(v_street)=0 or length(v_city)=0 then
raise exception 'обязательные поля street_address и city';
end if;

if exists (
select 1
from schema1.locations a
where a.street_address = v_street
and coalesce(a.postal_code,'') = coalesce(v_postal,'')
and a.city = v_city
and a.country_id = v_country_id
) then
raise exception 'такой адрес уже существует';
end if;

select coalesce(max(location_id), 0) + 1
into v_location_id
from schema1.locations;

insert into schema1.locations(location_id, street_address, postal_code, city, country_id)
values (v_location_id, v_street, v_postal, v_city, v_country_id);

raise info 'Адрес добавлен. location_id=%', v_location_id;
end;
$$;

call schema2.add_address('Pervomayskaya 10; 1010; Saint-P; 1');
call schema2.add_address('Lomonosova 15; 1010; Москва; 1');

select *
from schema1.locations
where postal_code = '1010';
-- адреса добавлены



-- 5. Создайте функцию. Функция должна принимать название города и
-- возвращать список подразделений, расположенных в указанном городе,
-- и количество сотрудников в этих подразделениях в виде json документ
-- следующего вида: {город: название_города, подразделения:
-- [{название:название_подразделения, сотрудники:[список сотрудников]},{}…]}.
-- Если в указанном городе нет подразделений функция должна возвращать
-- информационное сообщение

create function schema2.get_departments(p_city text)
returns json
language plpgsql
as $$
declare
dept_cnt int;
result json;
begin
if p_city is null or length(trim(p_city)) = 0 then
return json_build_object('message', 'Город не задан');
end if;

select count(*)
into dept_cnt
from schema1.departments d
join schema1.locations a
on a.location_id = d.location_id
where lower(a.city) = lower(trim(p_city));

if dept_cnt = 0 then
return json_build_object('message', format('В городе "%s" нет подразделений', trim(p_city)));
end if;

select json_build_object(
'город', trim(p_city),
'подразделения',
json_agg(
json_build_object(
'название', d.department_name,
'сотрудники',
coalesce(
(select json_agg(e.last_name || ' ' || e.first_name order by e.last_name, e.first_name)
from schema1.employees e
where e.department_id = d.department_id),
'[]'::json
)
)
order by d.department_name
)
)
into result
from schema1.departments d
join schema1.locations a
on a.location_id = d.location_id
where lower(a.city) = lower(trim(p_city));

return result;
end;
$$;

select schema2.get_departments('Paris');