Добавил:
ИВТ (советую зайти в "Несортированное") Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Скачиваний:
31
Добавлен:
23.10.2024
Размер:
34.05 Кб
Скачать
  1. Практическая часть

    Задание 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

  1. Напишите скрипт, формирующий таблицу со средним баллом по каждой дисциплине у преподавателей Института мпсу

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), чтобы вычислить средние оценки для каждого уникального сочетания профессора и предмета.

  1. Напишите скрипт, который случайным образом разбивает студентов на пары для вальса (мальчик/девочка). Мальчикам, которым не хватило пары в поле партнерши поставить значение «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 работает

  1. Создайте процедуру увеличения зарплаты преподавателя в зависимости от стажа. Стаж до 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 работает

  1. Создайте функцию, которая определяет разницу в возрасте между двумя студентами

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. Создайте функцию, рассчитывающую среднюю зарплату преподавателей в каждом структурном подразделении

Триггеры робит

  1. Создайте триггер, который запрещает добавление и изменение даты выдачи студенческого билета, у которого дата выдачи> даты по которую он действителен.

-- 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; -- Если запись уже существует

  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;

  1. Создание функции триггера

Эта функция будет обновлять поле updated_at на текущее время каждый раз, когда запись в таблице students изменяется.

CREATE OR REPLACE FUNCTION update_timestamp()

RETURNS TRIGGER AS $$

BEGIN

NEW.updated_at := CURRENT_TIMESTAMP; -- Устанавливаем текущее время

RETURN NEW; -- Возвращаем обновленную запись

END;

$$ LANGUAGE plpgsql;

  1. Создание триггера

Теперь создадим триггер, который будет вызывать функцию update_timestamp при каждом обновлении записи в таблице students.

CREATE TRIGGER update_students_timestamp

BEFORE UPDATE ON students

FOR EACH ROW

EXECUTE FUNCTION update_timestamp();

  1. Проверка работы триггера

Теперь, когда триггер создан, попробуем обновить запись в таблице 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 существующего студента