Загрузка данных
-- ============================================================
-- DATABASE: IT-куб — автоматизация приёма и распределения заявок
-- СУБД: MySQL 8.0+
-- Нормализация: 3НФ
-- ============================================================
DROP DATABASE IF EXISTS it_cube;
CREATE DATABASE it_cube
CHARACTER SET utf8mb4
COLLATE utf8mb4_unicode_ci;
USE it_cube;
-- ============================================================
-- 1. TABLE: parents — родители
-- ============================================================
CREATE TABLE parents (
parent_id INT AUTO_INCREMENT PRIMARY KEY, -- код_родителя
last_name VARCHAR(50) NOT NULL, -- фамилия
first_name VARCHAR(50) NOT NULL, -- имя
middle_name VARCHAR(50) NULL, -- отчество
phone VARCHAR(20) NOT NULL UNIQUE, -- телефон
email VARCHAR(100) NOT NULL UNIQUE, -- электронная_почта
snils CHAR(11) NULL UNIQUE, -- снилс
registered_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP -- дата_регистрации
) ENGINE=InnoDB;
-- ============================================================
-- 2. TABLE: children — дети
-- ============================================================
CREATE TABLE children (
child_id INT AUTO_INCREMENT PRIMARY KEY, -- код_ребёнка
last_name VARCHAR(50) NOT NULL, -- фамилия
first_name VARCHAR(50) NOT NULL, -- имя
middle_name VARCHAR(50) NULL, -- отчество
birth_date DATE NOT NULL, -- дата_рождения
snils CHAR(11) NULL UNIQUE, -- снилс
parent_id INT NOT NULL, -- код_родителя
CONSTRAINT fk_children_parents
FOREIGN KEY (parent_id)
REFERENCES parents(parent_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT chk_birth_date
CHECK (birth_date <= CURDATE())
) ENGINE=InnoDB;
-- ============================================================
-- 3. TABLE: teachers — преподаватели
-- ============================================================
CREATE TABLE teachers (
teacher_id INT AUTO_INCREMENT PRIMARY KEY, -- код_преподавателя
last_name VARCHAR(50) NOT NULL, -- фамилия
first_name VARCHAR(50) NOT NULL, -- имя
middle_name VARCHAR(50) NULL, -- отчество
specialization VARCHAR(100) NOT NULL, -- специализация
phone VARCHAR(20) NOT NULL UNIQUE, -- телефон
email VARCHAR(100) NOT NULL UNIQUE -- электронная_почта
) ENGINE=InnoDB;
-- ============================================================
-- 4. TABLE: programs — программы
-- ============================================================
CREATE TABLE programs (
program_id INT AUTO_INCREMENT PRIMARY KEY, -- код_программы
name VARCHAR(100) NOT NULL UNIQUE, -- название
description TEXT NULL, -- описание
min_age TINYINT UNSIGNED NOT NULL, -- возраст_мин
max_age TINYINT UNSIGNED NOT NULL, -- возраст_макс
duration_hours SMALLINT UNSIGNED NOT NULL, -- длительность_часов
CONSTRAINT chk_age
CHECK (min_age < max_age AND min_age >= 5)
) ENGINE=InnoDB;
-- ============================================================
-- 5. TABLE: groups — кружки (учебные группы)
-- ============================================================
CREATE TABLE groups (
group_id INT AUTO_INCREMENT PRIMARY KEY, -- код_кружка
program_id INT NOT NULL, -- код_программы
teacher_id INT NOT NULL, -- код_преподавателя
room VARCHAR(20) NULL, -- аудитория
academic_year VARCHAR(9) NOT NULL, -- учебный_год
max_seats SMALLINT UNSIGNED NOT NULL, -- макс_мест
free_seats SMALLINT UNSIGNED NOT NULL, -- свободных_мест
CONSTRAINT fk_groups_programs
FOREIGN KEY (program_id)
REFERENCES programs(program_id)
ON DELETE RESTRICT
ON UPDATE CASCADE,
CONSTRAINT fk_groups_teachers
FOREIGN KEY (teacher_id)
REFERENCES teachers(teacher_id)
ON DELETE RESTRICT
ON UPDATE CASCADE,
CONSTRAINT chk_seats
CHECK (free_seats <= max_seats AND free_seats >= 0)
) ENGINE=InnoDB;
-- ============================================================
-- 6. TABLE: schedule — расписание
-- ============================================================
CREATE TABLE schedule (
lesson_id INT AUTO_INCREMENT PRIMARY KEY, -- код_занятия
group_id INT NOT NULL, -- код_кружка
weekday ENUM('Пн','Вт','Ср','Чт','Пт','Сб','Вс') NOT NULL, -- день_недели
start_time TIME NOT NULL, -- время_начала
end_time TIME NOT NULL, -- время_окончания
CONSTRAINT fk_schedule_groups
FOREIGN KEY (group_id)
REFERENCES groups(group_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT chk_time
CHECK (start_time < end_time)
) ENGINE=InnoDB;
-- ============================================================
-- 7. TABLE: applications — заявки
-- ============================================================
CREATE TABLE applications (
application_id INT AUTO_INCREMENT PRIMARY KEY, -- код_заявки
child_id INT NOT NULL, -- код_ребёнка
group_id INT NULL, -- код_кружка
status VARCHAR(20) NOT NULL DEFAULT 'новая', -- статус
submitted_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, -- дата_подачи
updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP -- дата_изменения
ON UPDATE CURRENT_TIMESTAMP,
comment TEXT NULL, -- комментарий
CONSTRAINT fk_applications_children
FOREIGN KEY (child_id)
REFERENCES children(child_id)
ON DELETE CASCADE
ON UPDATE CASCADE,
CONSTRAINT fk_applications_groups
FOREIGN KEY (group_id)
REFERENCES groups(group_id)
ON DELETE SET NULL
ON UPDATE CASCADE,
CONSTRAINT chk_status
CHECK (status IN ('новая','на_проверке','одобрена',
'зачислен','отклонена','отменена')),
CONSTRAINT chk_enrolled_with_group
CHECK (
(status = 'зачислен' AND group_id IS NOT NULL)
OR
(status <> 'зачислен')
)
) ENGINE=InnoDB;
-- ============================================================
-- INDEXES — индексы
-- ============================================================
CREATE INDEX idx_children_parent ON children(parent_id); -- дети по родителю
CREATE INDEX idx_applications_child ON applications(child_id); -- заявки по ребёнку
CREATE INDEX idx_applications_group ON applications(group_id); -- заявки по кружку
CREATE INDEX idx_applications_status ON applications(status); -- заявки по статусу
CREATE INDEX idx_groups_program ON groups(program_id); -- кружки по программе
CREATE INDEX idx_groups_teacher ON groups(teacher_id); -- кружки по преподавателю
CREATE INDEX idx_schedule_group ON schedule(group_id); -- расписание по кружку
-- ============================================================
-- TRIGGERS — триггеры учёта свободных мест
-- ============================================================
DELIMITER $$
-- Проверка перед вставкой заявки
CREATE TRIGGER trg_applications_before_insert
BEFORE INSERT ON applications
FOR EACH ROW
BEGIN
IF NEW.status = 'зачислен' THEN
IF NEW.group_id IS NULL THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Нельзя зачислить без указания кружка';
END IF;
IF (SELECT free_seats FROM groups
WHERE group_id = NEW.group_id) <= 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Нет свободных мест в выбранном кружке';
END IF;
END IF;
END$$
-- Уменьшение мест после вставки зачисленной заявки
CREATE TRIGGER trg_applications_after_insert
AFTER INSERT ON applications
FOR EACH ROW
BEGIN
IF NEW.status = 'зачислен' AND NEW.group_id IS NOT NULL THEN
UPDATE groups
SET free_seats = free_seats - 1
WHERE group_id = NEW.group_id;
END IF;
END$$
-- Проверка перед обновлением заявки
CREATE TRIGGER trg_applications_before_update
BEFORE UPDATE ON applications
FOR EACH ROW
BEGIN
IF NEW.status = 'зачислен' AND OLD.status <> 'зачислен' THEN
IF NEW.group_id IS NULL THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Нельзя зачислить без указания кружка';
END IF;
IF (SELECT free_seats FROM groups
WHERE group_id = NEW.group_id) <= 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Нет свободных мест в выбранном кружке';
END IF;
END IF;
IF OLD.status = 'зачислен'
AND NEW.status = 'зачислен'
AND NEW.group_id <> OLD.group_id THEN
IF (SELECT free_seats FROM groups
WHERE group_id = NEW.group_id) <= 0 THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Нет свободных мест в новом кружке';
END IF;
END IF;
END$$
-- Корректировка мест после обновления заявки
CREATE TRIGGER trg_applications_after_update
AFTER UPDATE ON applications
FOR EACH ROW
BEGIN
IF OLD.status = 'зачислен'
AND NEW.status <> 'зачислен'
AND OLD.group_id IS NOT NULL THEN
UPDATE groups
SET free_seats = free_seats + 1
WHERE group_id = OLD.group_id;
END IF;
IF OLD.status <> 'зачислен'
AND NEW.status = 'зачислен'
AND NEW.group_id IS NOT NULL THEN
UPDATE groups
SET free_seats = free_seats - 1
WHERE group_id = NEW.group_id;
END IF;
IF OLD.status = 'зачислен'
AND NEW.status = 'зачислен'
AND OLD.group_id IS NOT NULL
AND NEW.group_id IS NOT NULL
AND OLD.group_id <> NEW.group_id THEN
UPDATE groups
SET free_seats = free_seats + 1
WHERE group_id = OLD.group_id;
UPDATE groups
SET free_seats = free_seats - 1
WHERE group_id = NEW.group_id;
END IF;
END$$
-- Освобождение места при удалении заявки
CREATE TRIGGER trg_applications_after_delete
AFTER DELETE ON applications
FOR EACH ROW
BEGIN
IF OLD.status = 'зачислен' AND OLD.group_id IS NOT NULL THEN
UPDATE groups
SET free_seats = free_seats + 1
WHERE group_id = OLD.group_id;
END IF;
END$$
-- Защита от удаления кружка с зачисленными детьми
CREATE TRIGGER trg_groups_before_delete
BEFORE DELETE ON groups
FOR EACH ROW
BEGIN
IF EXISTS (SELECT 1 FROM applications
WHERE group_id = OLD.group_id
AND status = 'зачислен') THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Нельзя удалить кружок: в нём есть зачисленные дети';
END IF;
END$$
-- Процедура отмены заявки
CREATE PROCEDURE cancel_application(IN p_application_id INT)
BEGIN
DECLARE v_status VARCHAR(20);
SELECT status INTO v_status
FROM applications
WHERE application_id = p_application_id;
IF v_status IS NULL THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Заявка не найдена';
END IF;
IF v_status = 'отменена' THEN
SIGNAL SQLSTATE '45000'
SET MESSAGE_TEXT = 'Заявка уже отменена';
END IF;
UPDATE applications
SET status = 'отменена',
group_id = NULL
WHERE application_id = p_application_id;
END$$
-- Процедура физического удаления заявки
CREATE PROCEDURE delete_application(IN p_application_id INT)
BEGIN
DELETE FROM applications WHERE application_id = p_application_id;
END$$
DELIMITER ;
-- ============================================================
-- END OF SCRIPT
-- ============================================================