Загрузка данных
SET GLOBAL log_bin_trust_function_creators = 1;
DROP DATABASE IF EXISTS library;
CREATE DATABASE library CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci;
USE library;
CREATE TABLE authors (
author_id INT PRIMARY KEY AUTO_INCREMENT,
full_name VARCHAR(150) NOT NULL,
country VARCHAR(100)
);
CREATE TABLE books (
book_id INT PRIMARY KEY AUTO_INCREMENT,
title VARCHAR(200) NOT NULL,
genre VARCHAR(50),
publication_year INT,
price DECIMAL(10,2),
rating DECIMAL(3,2)
);
CREATE TABLE book_authors (
book_id INT,
author_id INT,
PRIMARY KEY (book_id, author_id),
FOREIGN KEY (book_id) REFERENCES books(book_id),
FOREIGN KEY (author_id) REFERENCES authors(author_id)
);
CREATE TABLE readers (
reader_id INT PRIMARY KEY AUTO_INCREMENT,
full_name VARCHAR(150) NOT NULL,
age INT
);
CREATE TABLE loans (
loan_id INT PRIMARY KEY AUTO_INCREMENT,
book_id INT,
reader_id INT,
issue_date DATE NOT NULL,
return_date DATE,
fine DECIMAL(10,2) DEFAULT 0,
FOREIGN KEY (book_id) REFERENCES books(book_id),
FOREIGN KEY (reader_id) REFERENCES readers(reader_id)
);
-- Заполнение таблиц данными
INSERT INTO authors (full_name, country) VALUES
('Лев Толстой', 'Россия'),
('Фёдор Достоевский', 'Россия'),
('Джордж Оруэлл', 'Великобритания'),
('Айзек Азимов', 'США'),
('Артур Конан Дойл', 'Великобритания');
INSERT INTO books (title, genre, publication_year, price, rating) VALUES
('Война и мир', 'Роман', 1869, 950.00, 4.8),
('Анна Каренина', 'Роман', 1877, 780.00, 4.6),
('Преступление и наказание','Роман', 1866, 690.00, 4.7),
('1984', 'Антиутопия', 1949, 540.00, 4.9),
('Скотный двор', 'Антиутопия', 1945, 410.00, NULL),
('Я, робот', 'Фантастика', 1950, 620.00, 4.4),
('Основание', 'Фантастика', 1951, 700.00, 4.5),
('Записки о Шерлоке Холмсе','Детектив', 1892, 480.00, 4.3);
INSERT INTO book_authors (book_id, author_id) VALUES
(1,1),(2,1),(3,2),(4,3),(5,3),(6,4),(7,4),(8,5);
INSERT INTO readers (full_name, age) VALUES
('Иван Петров', 19),
('Мария Сидорова', 34),
('Олег Кузнецов', 67),
('Анна Смирнова', 25),
('Павел Орлов', 60);
INSERT INTO loans (book_id, reader_id, issue_date, return_date) VALUES
(1, 1, '2024-01-01', '2024-01-05'),
(1, 2, '2024-02-01', '2024-02-20'),
(1, 4, '2024-05-01', NULL),
(2, 1, '2024-03-10', '2024-03-15'),
(3, 3, '2024-04-01', '2024-04-12'),
(4, 1, '2024-06-01', NULL),
(6, 5, '2024-06-01', '2024-06-03'),
(1, 3, '2024-07-01', '2024-07-04'),
(2, 1, '2024-08-01', NULL);
-- ЗАДАНИЕ 1
-- Функция: GetBookLoanCount — принимает id книги, возвращает количество её выдач.
-- Процедура: ShowBookPopularity — проверяет существование книги, создаёт таблицу
-- temp_popularity и выводит популярность книги.
DELIMITER $$
CREATE FUNCTION GetBookLoanCount(p_book_id INT)
RETURNS INT
READS SQL DATA
BEGIN
DECLARE v_count INT;
SELECT COUNT(*) INTO v_count FROM loans WHERE book_id = p_book_id;
RETURN v_count;
END$$
CREATE PROCEDURE ShowBookPopularity(IN p_book_id INT)
BEGIN
DECLARE v_exists INT;
SELECT COUNT(*) INTO v_exists FROM books WHERE book_id = p_book_id;
IF v_exists = 0 THEN
SELECT CONCAT('Ошибка: книга с id = ', p_book_id, ' не найдена') AS message;
ELSE
DROP TEMPORARY TABLE IF EXISTS temp_popularity;
CREATE TEMPORARY TABLE temp_popularity (
book_id INT,
popularity INT );
INSERT INTO temp_popularity (book_id, popularity)
VALUES (p_book_id, GetBookLoanCount(p_book_id));
SELECT * FROM temp_popularity;
END IF;
END$$
DELIMITER ;
-- ЗАДАНИЕ 2
-- Функция: GetAgeCategory — принимает возраст, возвращает категорию. Процедура: UpdateAgeCategories — добавляет колонку age_category и обновляет её.
DELIMITER $$
CREATE FUNCTION GetAgeCategory(p_age INT)
RETURNS VARCHAR(20)
DETERMINISTIC
BEGIN
DECLARE v_category VARCHAR(20);
IF p_age < 25 THEN
SET v_category = 'Молодой';
ELSEIF p_age <= 60 THEN
SET v_category = 'Взрослый';
ELSE
SET v_category = 'Пенсионер';
END IF;
RETURN v_category;
END$$
CREATE PROCEDURE UpdateAgeCategories()
BEGIN
SET @col_exists = (
SELECT COUNT(*) FROM information_schema.columns
WHERE table_schema = DATABASE()
AND table_name = 'readers'
AND column_name = 'age_category'
);
IF @col_exists = 0 THEN
ALTER TABLE readers ADD COLUMN age_category VARCHAR(20);
END IF;
UPDATE readers SET age_category = GetAgeCategory(age);
SELECT reader_id, full_name, age, age_category FROM readers;
END$$
DELIMITER ;
-- ЗАДАНИЕ 3
-- Функция: CalculateFine — вычисляет штраф по дате выдачи и возврата.
-- Процедура: RecalculateFines — пересчитывает штрафы для проблемных выдач.
DELIMITER $$
CREATE FUNCTION CalculateFine(p_issue_date DATE, p_return_date DATE)
RETURNS DECIMAL(10,2)
DETERMINISTIC
BEGIN
DECLARE v_end DATE;
DECLARE v_days INT;
IF p_return_date IS NULL THEN
SET v_end = CURDATE();
ELSE
SET v_end = p_return_date;
END IF;
SET v_days = DATEDIFF(v_end, p_issue_date);
IF v_days <= 7 THEN
RETURN 0;
END IF;
RETURN (v_days - 7) * 1.00;
END$$
CREATE PROCEDURE RecalculateFines()
BEGIN
UPDATE loans
SET fine = CalculateFine(issue_date, return_date)
WHERE return_date IS NULL
OR DATEDIFF(return_date, issue_date) > 7;
SELECT loan_id, book_id, reader_id, issue_date, return_date, fine
FROM loans
WHERE return_date IS NULL OR fine > 0
ORDER BY fine DESC;
END$$
DELIMITER ;
-- ЗАДАНИЕ 4
-- Функция: GetAuthorAvgRating — вычисляет средний рейтинг книг автора.
-- Процедура: BuildAuthorsRating — создаёт таблицу с рейтингами авторов.
DELIMITER $$
CREATE FUNCTION GetAuthorAvgRating(p_author_id INT)
RETURNS DECIMAL(4,2)
READS SQL DATA
BEGIN
DECLARE v_avg DECIMAL(4,2);
SELECT AVG(b.rating) INTO v_avg
FROM books b
JOIN book_authors ba ON b.book_id = ba.book_id
WHERE ba.author_id = p_author_id
AND b.rating IS NOT NULL;
RETURN COALESCE(v_avg, 0);
END$$
CREATE PROCEDURE BuildAuthorsRating()
BEGIN
DROP TEMPORARY TABLE IF EXISTS author_rating;
CREATE TEMPORARY TABLE author_rating (
author_id INT,
author_name VARCHAR(150),
avg_rating DECIMAL(4,2)
);
INSERT INTO author_rating (author_id, author_name, avg_rating)
SELECT a.author_id, a.full_name, GetAuthorAvgRating(a.author_id)
FROM authors a;
SELECT * FROM author_rating ORDER BY avg_rating DESC;
END$$
DELIMITER ;
-- ЗАДАНИЕ 5
-- Функция: CountReaderGenre — считает, сколько раз читатель брал книги жанра.
-- Процедура: RecommendBooks — рекомендует топ-3 книги для читателя.
DELIMITER $$
CREATE FUNCTION CountReaderGenre(p_reader_id INT, p_genre VARCHAR(50))
RETURNS INT
READS SQL DATA
BEGIN
DECLARE v_count INT;
SELECT COUNT(*) INTO v_count
FROM loans l
JOIN books b ON l.book_id = b.book_id
WHERE l.reader_id = p_reader_id
AND b.genre = p_genre;
RETURN v_count;
END$$
CREATE PROCEDURE RecommendBooks(IN p_reader_id INT)
BEGIN
DECLARE v_exists INT;
SELECT COUNT(*) INTO v_exists FROM readers WHERE reader_id = p_reader_id;
IF v_exists = 0 THEN
SELECT CONCAT('Ошибка: читатель с id = ', p_reader_id, ' не найден') AS message;
ELSE
SELECT b.book_id,
b.title,
b.genre,
CountReaderGenre(p_reader_id, b.genre) AS genre_match
FROM books b
ORDER BY genre_match DESC, b.rating DESC
LIMIT 3;
END IF;
END$$
DELIMITER ;
-- ЗАДАНИЕ 6
-- Функция: SumBooksPriceAfterYear — суммирует цены книг после заданного года.
-- Процедура: SaveLibraryStats — сохраняет статистику в таблицу library_stats.
DELIMITER $$
CREATE FUNCTION SumBooksPriceAfterYear(p_year INT)
RETURNS DECIMAL(12,2)
READS SQL DATA
BEGIN
DECLARE v_sum DECIMAL(12,2);
SELECT SUM(price) INTO v_sum
FROM books
WHERE publication_year > p_year;
RETURN COALESCE(v_sum, 0);
END$$
CREATE PROCEDURE SaveLibraryStats(IN p_year INT)
BEGIN
DECLARE v_count INT;
DECLARE v_sum DECIMAL(12,2);
SELECT COUNT(*) INTO v_count FROM books WHERE publication_year > p_year;
IF v_count = 0 THEN
SELECT CONCAT('Нет книг, изданных после ', p_year, ' года') AS message;
ELSE
CREATE TABLE IF NOT EXISTS library_stats (
id INT PRIMARY KEY AUTO_INCREMENT,
after_year INT,
books_count INT,
total_price DECIMAL(12,2),
calc_date DATETIME DEFAULT CURRENT_TIMESTAMP
);
SET v_sum = SumBooksPriceAfterYear(p_year);
INSERT INTO library_stats (after_year, books_count, total_price)
VALUES (p_year, v_count, v_sum);
SELECT * FROM library_stats;
END IF;
END$$
DELIMITER ;
-- Вызов процедур для проверки
CALL ShowBookPopularity(1);
CALL ShowBookPopularity(999);
SET SQL_SAFE_UPDATES = 0;
CALL UpdateAgeCategories();
CALL UpdateAgeCategories();
CALL RecalculateFines();
CALL BuildAuthorsRating();
CALL RecommendBooks(1);
CALL SaveLibraryStats(1900);
SET SQL_SAFE_UPDATES = 1;