
- •Сборник запросов к учебной базе данных
- •Напишите скрипт, формирующий таблицу со средним баллом по каждой дисциплине у преподавателей Института мпсу
- •Напишите скрипт, который случайным образом разбивает студентов на пары для вальса (мальчик/девочка). Мальчикам, которым не хватило пары в поле партнерши поставить значение «Teddy bear :(»
- •Описание работы триггера
- •Контрольные вопросы
Практическая часть
Задание 1.
Напишите скрипт на языке PL/pgSQL, вычисляющий среднюю оценку студента. Аналогичный запрос напишите на языке SQL. Сравните время выполнения работы в обоих случаях. Для расчета времени выполнения скрипта, запустите его в терминале psql, перед этим запустив таймер с помощью команды \timing. Для того, чтобы отключить таймер после окончания работы, выполните команду \timing off.
Задание 2.
Напишите 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;
Объединение таблиц:
professors p: Основная таблица с информацией о профессорах.
employments e: Таблица, связывающая профессоров с их структурными единицами (например, кафедрами).
fields f: Таблица, содержащая информацию о полях (предметах), которые читают профессора.
field_comprehensions fc: Таблица, содержащая оценки по полям.
Условия фильтрации:
Запрос ограничен записями, где e.structural_unit_id = 1, что соответствует Институту МПСУ.
Агрегация данных:
Используется функция AVG(f.zet) для вычисления средней оценки для каждого поля.
Группировка: Данные группируются по имени профессора (last_name, first_name, patronymic) и названию поля (field_name), чтобы вычислить средние оценки для каждого уникального сочетания профессора и предмета.
Напишите скрипт, который случайным образом разбивает студентов на пары для вальса (мальчик/девочка). Мальчикам, которым не хватило пары в поле партнерши поставить значение «Teddy bear :(»
-- 1. Добавляем поле partner
ALTER TABLE students ADD COLUMN IF NOT EXISTS partner TEXT;
-- 2. Обновляем поле partner для всех студентов, очищая его перед распределением пар
UPDATE students
SET partner = NULL;
-- 3. Создаем временные списки "мальчиков" и "девочек" на основе отчества и фамилии
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 !~ '[аеёиоуыэюя]$') -- Фамилия заканчивается на согласную
OR (patronymic IS NULL AND last_name IS NOT 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 ~ '[аеёиоуыэюя]$') -- Фамилия заканчивается на гласную
)
-- 4. Соединяем мальчиков и девочек по случайному порядку и обновляем поле partner
UPDATE students b
SET partner = COALESCE(g.first_name ||’’|| '' g.last_name, 'Teddy bear :(')
FROM boys b2
LEFT JOIN girls g ON b2.row_num = g.row_num
WHERE b.student_id = b2.student_id;
-- 5. Дополнительно обновляем мальчиков, которым не хватило девочки
UPDATE students b
SET partner = 'Teddy bear :('
WHERE partner IS NULL
AND ((patronymic IS NOT NULL AND patronymic LIKE '%ч')
OR (patronymic IS NULL AND last_name !~ '[аеёиоуыэюя]$')); -- Только для мальчиков
-- 6. Дополнительно обновляем девочек, которым не хватило мальчика
UPDATE students g
SET partner = 'Teddy bear :('
WHERE partner IS NULL
AND ((patronymic IS NOT NULL AND patronymic LIKE '%а')
OR (patronymic IS NULL AND last_name ~ '[аеёиоуыэюя]$')); -- Только для девочек
Добавление поля partner
. Очистка поля partner
…
Что делает Создает два временных списка:
boys:Включает мальчиков, основываясь на критериях:
Если есть отчество, оно должно заканчиваться на "ч".
Если отчество отсутствует, фамилия должна заканчиваться на согласную.
girls:Включает девочек, основываясь на критериях:
Если есть отчество, оно должно заканчиваться на "а".
Если отчество отсутствует, фамилия должна заканчиваться на гласную.
ROWNUMBER () OVER (ORDER BY RANDOM()) создает случайный номер для каждого студента в списке.
…..
Что делает: Обновляет поле partner для мальчиков:
Использует LEFT JOIN для соединения списков мальчиков и девочек на основе случайных номеров (row_num).
Устанавливает partner равным имени и фамилии девочки (с помощью COALESCE, если девочка не найдена, устанавливается значение 'Teddy bear :(').
Процедуры на языке SQL работает
Создайте процедуру увеличения зарплаты преподавателя в зависимости от стажа. Стаж до 10 лет – увеличиваем на a, от 11 до 20 – на 2a, более на 3a рублей. Где a входной параметр процедуры
CREATE OR REPLACE PROCEDURE increase_salary(
p_professor_id INT
)
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 + 100000;
ELSIF v_experience >= 10 AND v_experience <= 20 THEN
v_current_salary := v_current_salary + 2 * 100000;
ELSE
v_current_salary := v_current_salary + 3 * 100000;
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(1); -- Заменить 1 на ID нужного профессора
Функции на языке SQL работает
Создайте функцию, которая определяет разницу в возрасте между двумя студентами
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); -- Например, если 1 и 2 — идентификаторы студентов
Создайте функцию, рассчитывающую среднюю зарплату преподавателей в каждом структурном подразделении
Триггеры робит
Создайте триггер, который запрещает добавление и изменение даты выдачи студенческого билета, у которого дата выдачи> даты по которую он действителен.
-- 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 = '2025-01-01'
WHERE student_id = 1; -- Если запись уже существует
Создайте триггер, не позволяющий вводить дубли в таблицу студентов (совпадение фамилии, имени и отчества с точностью до двух букв в каждом). В предупреждении выведите ФИО студента с похожими данными. Используйте функцию levenshtein().
(не уверен что работает)
-- 1. Создаем расширение для работы с функцией levenshtein
CREATE EXTENSION IF NOT EXISTS fuzzystrmatch;
-- 2. Создаем функцию триггера
CREATE OR REPLACE FUNCTION prevent_duplicates()
RETURNS TRIGGER AS $$
DECLARE
similar_student RECORD;
BEGIN
-- Проверяем, есть ли уже студент с похожими данными
SELECT *
INTO similar_student
FROM students
WHERE levenshtein(NEW.last_name, last_name) <= 2
AND levenshtein(NEW.first_name, first_name) <= 2
AND levenshtein(NEW.patronymic, patronymic) <= 2
LIMIT 1; -- Берем только одного похожего студента
-- Если найден похожий студент, выбрасываем исключение
IF FOUND THEN
RAISE EXCEPTION 'Студент с похожими данными уже существует: % % %',
similar_student.last_name,
similar_student.first_name,
similar_student.patronymic;
END IF;
RETURN NEW; -- Возвращаем новую запись
END;
$$ LANGUAGE plpgsql;
-- 3. Создаем триггер
CREATE TRIGGER prevent_student_duplicates
BEFORE INSERT ON students
FOR EACH ROW
EXECUTE FUNCTION prevent_duplicates();
-- Пример вставки, который должен вызвать ошибку
INSERT INTO students (last_name, first_name, patronymic)
VALUES ('Иванов', 'Иван', 'Иванович'); -- Предполагается, что такой студент уже существует с малым отличиемЗадание 3.
INSERT INTO students (student_id, last_name, first_name, patronymic, students_group_number, birthday, email, ticket_color, new_student_id, partner)
VALUES ('8210190', 'Иванов', 'Иван', 'Иванович', 'Группа 1', '2000-01-01', 'ivanov@example.com', 'синий', NULL, NULL);
создайте любой триггер.
1)Добавление поля updated_at
ALTER TABLE students ADD COLUMN IF NOT EXISTS updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP;
Создание функции триггера
Эта функция будет обновлять поле updated_at на текущее время каждый раз, когда запись в таблице students изменяется.
CREATE OR REPLACE FUNCTION update_timestamp()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at := CURRENT_TIMESTAMP; -- Устанавливаем текущее время
RETURN NEW; -- Возвращаем обновленную запись
END;
$$ LANGUAGE plpgsql;
Создание триггера
Теперь создадим триггер, который будет вызывать функцию update_timestamp при каждом обновлении записи в таблице students.
CREATE TRIGGER update_students_timestamp
BEFORE UPDATE ON students
FOR EACH ROW
EXECUTE FUNCTION update_timestamp();
Проверка работы триггера
Теперь, когда триггер создан, попробуем обновить запись в таблице students и проверим, что поле updated_at изменилось:
sql
Копировать код
-- Пример обновления записи
UPDATE students
SET last_name = 'Петров'
WHERE student_id = 1; -- ID существующего студента
-- Проверка изменения поля updated_at
SELECT student_id, last_name, updated_at
FROM students
WHERE student_id = 1; -- ID существующего студента