
Практическая часть Задание 1.
Напишите SQL запросы к учебной базе данных в соответствии с вариантом. Вариант к практической части выбирается по формуле: V = (N % 10) +1, где N – номер в списке группы, % - остаток от деления.
№ варианта |
№ запросов |
3 |
3, 13, 23, 33, 43, 53, 63 |
Сборник запросов к учебной базе данных
Скрипты на языке PL/pgSQL
Напишите скрипт, формирующий таблицу со средним баллом по каждой дисциплине у преподавателей Института МПСУ
SELECT
p.last_name ||' '|| p.first_name ||' '|| p.patronymic AS professor_name,
f.field_name,
AVG(f.zet) AS average_grade
FROM
professors p
JOIN
employments e ON p.professor_id = e.professor_id
JOIN
fields f ON e.structural_unit_id = f.structural_unit_id
JOIN
field_comprehensions fc ON f.field_id = fc.field
WHERE
e.structural_unit_id = 1 -- Здесь 1 соответствует Институту МПСУ
GROUP BY
p.last_name, p.first_name, p.patronymic, f.field_name;
Напишите скрипт, который случайным образом разбивает студентов на пары для вальса (мальчик/девочка). Мальчикам, которым не хватило пары в поле партнерши поставить значение «Teddy bear :(»
ALTER TABLE students ADD COLUMN IF NOT EXISTS partner TEXT;
UPDATE students
SET partner = NULL;
CREATE TEMPORARY TABLE temp_pairs AS
WITH boys AS (
SELECT student_id, first_name, last_name, ROW_NUMBER() OVER (ORDER BY RANDOM()) AS row_num
FROM students
WHERE
(patronymic IS NOT NULL AND patronymic LIKE '%ч')
OR (patronymic IS NULL AND last_name !~ '[аеёиоуыэюя]$')
),
girls AS (
SELECT student_id, first_name, last_name, ROW_NUMBER() OVER (ORDER BY RANDOM()) AS row_num
FROM students
WHERE
(patronymic IS NOT NULL AND patronymic LIKE '%а')
OR (patronymic IS NULL AND last_name ~ '[аеёиоуыэюя]$')
)
SELECT
b.student_id AS boy_id,
b.first_name AS boy_first_name,
b.last_name AS boy_last_name,
g.student_id AS girl_id,
g.first_name AS girl_first_name,
g.last_name AS girl_last_name
FROM boys b
LEFT JOIN girls g ON b.row_num = g.row_num;
UPDATE students s
SET partner = COALESCE(
(SELECT p.girl_first_name || ' ' || p.girl_last_name
FROM temp_pairs p
WHERE p.boy_id = s.student_id),
'Teddy bear :('
)
WHERE s.student_id IN (SELECT boy_id FROM temp_pairs);
UPDATE students s
SET partner = COALESCE(
(SELECT p.boy_first_name || ' ' || p.boy_last_name
FROM temp_pairs p
WHERE p.girl_id = s.student_id),
'Teddy bear :('
)
WHERE s.student_id IN (SELECT girl_id FROM temp_pairs WHERE girl_id IS NOT NULL);
DROP TABLE temp_pairs;
Создайте процедуру увеличения зарплаты преподавателя в зависимости от стажа. Стаж до 10 лет – увеличиваем на a, от 11 до 20 – на 2a, более на 3a рублей. Где a входной параметр процедуры
CREATE OR REPLACE PROCEDURE increase_salary(
p_professor_id INT,
a NUMERIC -- New parameter for the salary increase amount
)
LANGUAGE plpgsql
AS $$
DECLARE
v_experience INT;
v_current_salary NUMERIC;
BEGIN
-- Получаем стаж и текущую зарплату преподавателя
SELECT
experience, salary
INTO
v_experience, v_current_salary
FROM
professors
WHERE
professor_id = p_professor_id;
-- Проверяем, существует ли преподаватель
IF NOT FOUND THEN
RAISE EXCEPTION 'Professor with ID % not found', p_professor_id;
END IF;
-- Увеличиваем зарплату в зависимости от стажа
IF v_experience < 10 THEN
v_current_salary := v_current_salary + a;
ELSIF v_experience >= 10 AND v_experience <= 20 THEN
v_current_salary := v_current_salary + 2 * a;
ELSE
v_current_salary := v_current_salary + 3 * a;
END IF;
-- Обновляем зарплату в таблице
UPDATE professors
SET salary = v_current_salary
WHERE professor_id = p_professor_id;
RAISE NOTICE 'Salary for professor ID % updated to %', p_professor_id, v_current_salary;
END;
$$
--вызов процедуры для конкретного препода
CALL increase_salary(id, salary); --
Создайте функцию, которая определяет разницу в возрасте между двумя студентами
CREATE OR REPLACE FUNCTION age_difference(student_id1 INT, student_id2 INT)
RETURNS INT AS $$
DECLARE
birthday1 DATE;
birthday2 DATE;
age_diff INT;
BEGIN
-- Получаем даты рождения двух студентов
SELECT birthday INTO birthday1 FROM students WHERE student_id = student_id1;
SELECT birthday INTO birthday2 FROM students WHERE student_id = student_id2;
-- Проверка на существование студентов
IF birthday1 IS NULL OR birthday2 IS NULL THEN
RAISE EXCEPTION 'One or both student IDs do not exist.';
END IF;
-- Вычисляем разницу в возрасте
age_diff := EXTRACT(YEAR FROM AGE(birthday1)) - EXTRACT(YEAR FROM AGE(birthday2));
-- Возвращаем абсолютное значение разницы
RETURN ABS(age_diff);
END;
$$ LANGUAGE plpgsql;
SELECT age_difference(1, 2);
Создайте функцию, рассчитывающую среднюю зарплату преподавателей в каждом структурном подразделении
CREATE OR REPLACE FUNCTION calculate_department_median_salaries()
RETURNS TABLE (
department_id INTEGER,
department_name TEXT,
median_salary NUMERIC
) AS $$
BEGIN
RETURN QUERY
WITH ranked_salaries AS (
SELECT
su.structural_unit_id,
su.full_title,
(p.salary::NUMERIC) AS salary_amount,
ROW_NUMBER() OVER (PARTITION BY su.structural_unit_id ORDER BY (p.salary::NUMERIC)) AS row_num,
COUNT(*) OVER (PARTITION BY su.structural_unit_id) AS total_count
FROM
Structural_units su
JOIN
Employments e ON su.structural_unit_id = e.structural_unit_id
JOIN
Professors p ON e.professor_id = p.professor_id
)
SELECT
rs.structural_unit_id AS department_id,
rs.full_title AS department_name,
AVG(rs.salary_amount) AS median_salary
FROM
ranked_salaries rs
WHERE
rs.row_num IN (rs.total_count/2, (rs.total_count+1)/2)
GROUP BY
rs.structural_unit_id, rs.full_title
ORDER BY
rs.structural_unit_id;
END;
$$ LANGUAGE plpgsql;
Создайте триггер, который запрещает добавление и изменение даты выдачи студенческого билета, у которого дата выдачи> даты по которую он действителен.
-- 1. Создаем функцию триггера
CREATE OR REPLACE FUNCTION check_issue_date()
RETURNS TRIGGER AS $$
BEGIN
-- Проверяем, что дата выдачи не превышает дату окончания действия
IF NEW.issue_date > NEW.expiration_date THEN
RAISE EXCEPTION 'Дата выдачи не может быть больше даты окончания действия';
END IF;
RETURN NEW; -- Возвращаем новую запись
END;
$$ LANGUAGE plpgsql;
-- 2. Создаем триггер
CREATE TRIGGER issue_date_trigger
BEFORE INSERT OR UPDATE ON student_ids
FOR EACH ROW
EXECUTE FUNCTION check_issue_date();
INSERT INTO student_ids (student_id, issue_date, expiration_date)
VALUES (1, '2025-01-01', '2024-12-31'); -- Это вызовет ошибку
UPDATE student_ids
SET issue_date = '2028-01-01'
WHERE student_id = 863954;
Создайте триггер, не позволяющий вводить дубли в таблицу студентов (совпадение фамилии, имени и отчества с точностью до двух букв в каждом). В предупреждении выведите ФИО студента с похожими данными. Используйте функцию levenshtein().
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
CREATE OR REPLACE FUNCTION prevent_similar_students()
RETURNS TRIGGER AS $$
DECLARE
similar_student RECORD;
error_message TEXT;
BEGIN
SELECT INTO similar_student s.*
FROM Students s
WHERE
levenshtein(lower(NEW.last_name), lower(s.last_name)) <= 2 AND
levenshtein(lower(NEW.first_name), lower(s.first_name)) <= 2 AND
(
(NEW.patronymic IS NULL AND s.patronymic IS NULL) OR
(NEW.patronymic IS NOT NULL AND s.patronymic IS NOT NULL AND
levenshtein(lower(NEW.patronymic), lower(s.patronymic)) <= 2)
)
LIMIT 1;
IF FOUND THEN
error_message := format(
'Cannot insert student: similar student already exists (ID: %s, Name: %s %s %s)',
similar_student.student_id,
similar_student.last_name,
similar_student.first_name,
COALESCE(similar_student.patronymic, '')
);
RAISE EXCEPTION '%', error_message;
END IF;
RETURN NEW;
END;
$$ LANGUAGE plpgsql;
CREATE TRIGGER check_similar_students
BEFORE INSERT OR UPDATE ON Students
FOR EACH ROW EXECUTE FUNCTION prevent_similar_students();
INSERT INTO students (student_id, last_name, first_name, patronymic, students_group_number, birthday, email, partner)
VALUES ('863954', 'Жадов', 'Василий', 'Николаич', 'ИТД-42', '2000-01-01', 'email@example.com', NULL);