csdesign-smart-library/sql/admin_query.sql
2025-06-24 10:56:21 +08:00

469 lines
21 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- 假设当前操作的管理员ID
-- 在实际应用中这个ID会从登录会话中获取
-- SELECT admin_id INTO v_admin_id FROM admins WHERE emp_no = 'A001'; -- 示例获取张管理的ID
-- 为了简化示例,我们直接使用数字,假设 admin_id=1 是一个有效管理员
-- 请根据你的 admins 表实际数据替换
----------------------------------------------------------------------
-- (1). 图书信息管理(增删改查、批量导入)
----------------------------------------------------------------------
-- A. 新增图书 (Create)
INSERT INTO books (isbn, title, authors, publisher, publish_date, price, classification_no, location, total_copies, available_copies, description, cover_url)
VALUES
('9787121408881', 'Effective Java 中文版 (原书第3版)', ARRAY['Joshua Bloch'], '机械工业出版社', '2019-01-01', 119.00, 'TP312JA', 'A区3架5层', 5, 5, 'Java程序员必读的经典著作包含90个实用条目。', 'http://example.com/cover_effective_java.jpg');
-- B. 查询图书 (Read)
-- B1. 按ISBN精确查询
SELECT * FROM books WHERE isbn = '9787111624927';
-- B2. 按书名模糊查询 (使用 trigram 索引 idx_books_title_trgm)
SELECT * FROM books WHERE title % '计算机'; -- 查询包含“计算机”的
SELECT * FROM books WHERE title ILIKE '%系统%'; -- 不区分大小写查询包含“系统”的
-- B3. 按作者查询 (使用 GIN 索引 idx_books_authors_gin)
SELECT * FROM books WHERE authors @> ARRAY['吴军']; -- 查询作者包含“吴军”的
-- B4. 按分类号查询
SELECT * FROM books WHERE classification_no = 'TP301';
-- B5. 查看所有“正常”状态的图书
SELECT * FROM books WHERE status = 'normal';
-- C. 修改图书信息 (Update)
-- 假设要修改 book_id 为 1 的图书信息
UPDATE books
SET
price = 145.00,
location = 'A区2架3层 (更新)',
description = '计算机系统的经典之作,最新修订版。',
updated_at = now()
WHERE book_id = 1; -- 假设 book_id 为 1 的是《深入理解计算机系统》
-- 修改图书状态 (例如,将一本损坏的书修复后改为正常)
UPDATE books
SET status = 'normal', available_copies = available_copies + 1 -- 如果之前因损坏不可借
WHERE isbn = '9787208159659' AND status = 'damaged'; -- 假设《三体全集》某本修复
-- D. 删除图书 (Delete) - 谨慎操作,通常建议逻辑删除或下架
-- 注意:如果该书有关联的借阅记录、预约记录、评价记录,且外键设置了 ON DELETE CASCADE则这些关联记录也会被删除。
-- 如果设置了 ON DELETE RESTRICT (或默认),且存在关联记录,则删除会失败。
-- 你的表结构中borrow_records, reservations, reviews 都对 book_id 设置了 ON DELETE CASCADE。
-- DELETE FROM books WHERE isbn = '9780132350884'; -- 删除已下架的《Clean Code》
-- 更好的方式是“下架”
UPDATE books
SET status = 'removed', available_copies = 0
WHERE isbn = '9780132350884';
-- E. 批量导入图书 (Batch Import)
INSERT INTO books (isbn, title, authors, publisher, total_copies, available_copies) VALUES
('ISBN001', '批量导入书1', ARRAY['作者X'], '出版社A', 2, 2),
('ISBN002', '批量导入书2', ARRAY['作者Y', '作者Z'], '出版社B', 3, 3);
----------------------------------------------------------------------
-- (2). 学生账户管理
----------------------------------------------------------------------
-- A. 新增学生账户
INSERT INTO students (stu_no, name, gender, department, major, grade, class, phone, email, max_borrow)
VALUES
('S2024001', '刘新', 'M', '物理学院', '应用物理', '2024', '01班', '13600001111', 'liuxin@example.com', (SELECT setting_value::int FROM system_settings WHERE setting_key='max_borrow_default'));
-- B. 查询学生账户
-- B1. 按学号查询
SELECT * FROM students WHERE stu_no = 'S2023001';
-- B2. 按姓名模糊查询
SELECT * FROM students WHERE name LIKE '张%';
-- B3. 查询某院系所有学生
SELECT * FROM students WHERE department = '计算机学院';
-- C. 修改学生账户信息
-- C1. 修改学生联系方式
UPDATE students
SET phone = '13511112222', email = 'new_zhangsan@example.com'
WHERE stu_no = 'S2023001';
-- C2. 修改学生账户状态 (例如:解挂、冻结/解冻 - 冻结通常由触发器完成,但管理员也可手动操作)
-- 解挂
UPDATE students SET account_status = 'active' WHERE stu_no = 'S2023005' AND account_status = 'reported';
-- 手动冻结 (如果业务需要)
UPDATE students SET account_status = 'frozen' WHERE stu_no = 'S2023001';
-- 手动解冻 (例如,学生缴清罚款后,触发器未及时更新或有特殊情况)
-- 首先确保罚款已清或问题已解决
-- UPDATE fines SET status = 'paid' WHERE student_id = (SELECT student_id FROM students WHERE stu_no = 'S2023005') AND status = 'unpaid';
-- 然后(如果触发器没有将所有欠款清零后的账户自动激活)
-- UPDATE students SET account_status = 'active' WHERE stu_no = 'S2023005' AND account_status = 'frozen';
-- C3. 修改学生最大借阅量
UPDATE students SET max_borrow = 12 WHERE stu_no = 'S2023001';
----------------------------------------------------------------------
-- (3). 处理借阅、归还、续借请求
----------------------------------------------------------------------
-- A. 处理借阅请求 (学生在前台操作,管理员后台处理或确认)
-- 假设学生 (stu_no='S2023002', 李四) 要借阅图书 (isbn='9787111624927', 深入理解计算机系统)
-- 前提检查 (通常在应用层完成,但也可在存储过程中封装):
-- 1. 图书可借: SELECT available_copies, status FROM books WHERE isbn = '9787111624927'; (available_copies > 0 and status = 'normal')
-- 2. 学生账户正常: SELECT account_status FROM students WHERE stu_no = 'S2023002'; (account_status = 'active')
-- 3. 学生未超借阅上限: SELECT current_borrow, max_borrow FROM students WHERE stu_no = 'S2023002'; (current_borrow < max_borrow)
DO $$
DECLARE
v_student_id bigint;
v_book_id bigint;
v_available_copies int;
v_book_status book_status_enum;
v_account_status acct_status_enum;
v_current_borrow int;
v_max_borrow int;
v_borrow_duration int;
BEGIN
SELECT student_id, account_status, current_borrow, max_borrow INTO v_student_id, v_account_status, v_current_borrow, v_max_borrow
FROM students WHERE stu_no = 'S2023002';
SELECT book_id, available_copies, status INTO v_book_id, v_available_copies, v_book_status
FROM books WHERE isbn = '9787111624927';
IF NOT FOUND THEN RAISE EXCEPTION '学生或图书不存在'; END IF;
IF v_book_status != 'normal' THEN RAISE EXCEPTION '图书状态异常,不可借阅: %', v_book_status; END IF;
IF v_available_copies <= 0 THEN RAISE EXCEPTION '图书已无馆藏可借'; END IF;
IF v_account_status != 'active' THEN RAISE EXCEPTION '学生账户状态异常,不可借阅: %', v_account_status; END IF;
IF v_current_borrow >= v_max_borrow THEN RAISE EXCEPTION '已达到最大借阅量'; END IF;
SELECT setting_value::int INTO v_borrow_duration FROM system_settings WHERE setting_key = 'borrow_duration_days';
INSERT INTO borrow_records (book_id, student_id, borrow_date, due_date, status)
VALUES (v_book_id, v_student_id, current_date, current_date + (v_borrow_duration || ' days')::interval, 'borrowed');
RAISE NOTICE '借阅成功! Book ID: %, Student ID: %', v_book_id, v_student_id;
-- 触发器 trg_sync_book_student 会自动更新 books.available_copies 和 students.current_borrow
END $$;
-- B. 处理归还请求
-- 假设要归还 borrow_id 为 (假设是1根据实际情况查询得到) 的借阅记录
-- SELECT borrow_id FROM borrow_records br JOIN students s ON br.student_id = s.student_id JOIN books b ON br.book_id = b.book_id
-- WHERE s.stu_no='S2023001' AND b.isbn='9787111624927' AND br.status IN ('borrowed', 'overdue'); (假设这是张三借的《深入理解计算机系统》)
-- 获取特定借阅记录ID (例如,张三当前借阅的《深入理解计算机系统》)
DO $$
DECLARE
v_borrow_id bigint;
BEGIN
SELECT br.borrow_id INTO v_borrow_id
FROM borrow_records br
JOIN students s ON br.student_id = s.student_id
JOIN books b ON br.book_id = b.book_id
WHERE s.stu_no = 'S2023001' AND b.isbn = '9787111624927' AND br.status IN ('borrowed', 'overdue')
ORDER BY br.borrow_date DESC LIMIT 1; -- 获取最近一次未还的
IF NOT FOUND THEN
RAISE NOTICE '未找到该学生对应的此书的当前借阅记录。';
RETURN;
END IF;
RAISE NOTICE '正在归还 borrow_id: %', v_borrow_id;
UPDATE borrow_records
SET
status = 'returned',
return_date = current_date
WHERE borrow_id = v_borrow_id;
-- 触发器 trg_calc_fine 会在逾期归还时自动记录罚款
-- 触发器 trg_sync_book_student 会自动更新 books.available_copies 和 students.current_borrow
RAISE NOTICE '归还操作完成。';
END $$;
-- C. 处理续借请求
-- 假设要续借 borrow_id 为 (假设是2李四借的《数学之美》) 的借阅记录
-- 续借条件检查 (应用层或存储过程):
-- 1. 图书未被其他人预约: SELECT 1 FROM reservations WHERE book_id = _book_id_ AND status = 'waiting'; (应为空)
-- 2. 未超最大续借次数: SELECT renew_times FROM borrow_records WHERE borrow_id = _borrow_id_;
-- (renew_times < (SELECT setting_value::int FROM system_settings WHERE setting_key = 'max_renew_times'))
-- 3. 学生账户正常,图书未逾期等。
DO $$
DECLARE
v_borrow_id bigint;
v_book_id bigint;
v_renew_times int;
v_max_renew_times int;
v_is_reserved int;
v_borrow_duration int;
v_current_due_date date;
v_current_status borrow_status_enum;
BEGIN
-- 假设李四 (S2023002) 续借《数学之美》 (isbn='9787030598007')
SELECT br.borrow_id, br.book_id, br.renew_times, br.due_date, br.status
INTO v_borrow_id, v_book_id, v_renew_times, v_current_due_date, v_current_status
FROM borrow_records br
JOIN students s ON br.student_id = s.student_id
JOIN books b ON br.book_id = b.book_id
WHERE s.stu_no = 'S2023002' AND b.isbn = '9787030598007' AND br.status = 'borrowed' -- 通常只能续借未逾期的
ORDER BY br.borrow_date DESC LIMIT 1;
IF NOT FOUND THEN RAISE EXCEPTION '未找到该借阅记录或记录状态不符。'; END IF;
IF v_current_status = 'overdue' THEN RAISE EXCEPTION '图书已逾期,不可续借,请先处理逾期。'; END IF;
SELECT setting_value::int INTO v_max_renew_times FROM system_settings WHERE setting_key = 'max_renew_times';
SELECT setting_value::int INTO v_borrow_duration FROM system_settings WHERE setting_key = 'borrow_duration_days';
IF v_renew_times >= v_max_renew_times THEN
RAISE EXCEPTION '已达到最大续借次数 (%)', v_max_renew_times;
END IF;
SELECT count(*) INTO v_is_reserved FROM reservations
WHERE book_id = v_book_id AND status = 'waiting'
AND student_id != (SELECT student_id FROM students WHERE stu_no = 'S2023002'); -- 排除自己的预约
IF v_is_reserved > 0 THEN
RAISE EXCEPTION '该图书已被他人预约,不可续借。';
END IF;
UPDATE borrow_records
SET
due_date = v_current_due_date + (v_borrow_duration || ' days')::interval, -- 从当前应还日期开始延长
renew_times = v_renew_times + 1
WHERE borrow_id = v_borrow_id;
RAISE NOTICE '续借成功! Borrow ID: %,新应还日期: %', v_borrow_id, (SELECT due_date FROM borrow_records WHERE borrow_id=v_borrow_id);
END $$;
----------------------------------------------------------------------
-- (4). 管理预约队列
----------------------------------------------------------------------
-- A. 查看某本图书的预约队列 (例如 book_id = 7, Java核心技术)
SELECT r.reservation_id, r.reserve_date, r.status, s.stu_no, s.name as student_name, s.email
FROM reservations r
JOIN students s ON r.student_id = s.student_id
WHERE r.book_id = 7 AND r.status = 'waiting' -- 假设book_id 7是《Java核心技术 卷I》
ORDER BY r.reserve_date ASC; -- 按预约日期升序,先预约的在前
-- B. 处理到书通知 (当一本被预约的书归还后)
-- 假设 book_id = 7 的书有副本归还,管理员通知队列中的第一个学生
-- B1. 找到第一个等待的学生
DO $$
DECLARE
v_reservation_id bigint;
v_student_id bigint;
v_book_id int := 7; -- 假设是 Java 核心技术
v_reservation_expiry_days int;
BEGIN
SELECT reservation_id, student_id INTO v_reservation_id, v_student_id
FROM reservations
WHERE book_id = v_book_id AND status = 'waiting'
ORDER BY reserve_date ASC
LIMIT 1;
IF FOUND THEN
SELECT setting_value::int INTO v_reservation_expiry_days
FROM system_settings WHERE setting_key = 'reservation_expiry_days';
UPDATE reservations
SET status = 'available' -- (可以增加一个 notified_at timestamp 和 available_until timestamp 字段)
-- available_until = current_timestamp + (v_reservation_expiry_days || ' days')::interval -- (如果表有此字段)
WHERE reservation_id = v_reservation_id;
RAISE NOTICE '已通知学生ID % (预约ID %) 图书 % 可取。', v_student_id, v_reservation_id, v_book_id;
-- 实际应用中会发送邮件/短信
ELSE
RAISE NOTICE '图书 % 没有等待中的预约。', v_book_id;
END IF;
END $$;
-- C. 学生未在规定时间内取书,预约自动或手动过期
UPDATE reservations
SET status = 'expired'
WHERE status = 'available' AND reservation_id = _reservation_id_ ;
-- AND available_until < current_timestamp; -- (如果表有 available_until 字段)
-- D. 管理员取消某个预约 (例如,学生请求取消)
UPDATE reservations
SET status = 'cancelled'
WHERE reservation_id = _reservation_id_ ; -- 替换为实际预约ID
----------------------------------------------------------------------
-- (5). 处理图书遗失、损坏等异常情况
----------------------------------------------------------------------
-- A. 处理图书遗失
-- 假设学生 (stu_no='S2022006', 周八) 遗失了图书 (isbn='9787559620187', 明朝那些事儿)
-- A1. 更新借阅记录状态 (如果该书是被借阅后遗失的)
-- 你已经在测试数据中插入了一条 borrow_records 状态为 'lost' 的记录,这里假设是管理员新发现的遗失
DO $$
DECLARE
v_student_id bigint;
v_book_id bigint;
v_borrow_id bigint;
v_book_price numeric;
v_admin_id bigint := (SELECT admin_id FROM admins WHERE emp_no = 'A002'); -- 操作管理员ID
BEGIN
SELECT student_id INTO v_student_id FROM students WHERE stu_no = 'S2022006';
SELECT book_id, price INTO v_book_id, v_book_price FROM books WHERE isbn = '9787559620187';
-- 查找该学生对这本书的未还借阅记录
SELECT borrow_id INTO v_borrow_id
FROM borrow_records
WHERE student_id = v_student_id AND book_id = v_book_id AND status IN ('borrowed', 'overdue')
LIMIT 1;
IF FOUND THEN
UPDATE borrow_records SET status = 'lost' WHERE borrow_id = v_borrow_id;
RAISE NOTICE '借阅记录 % 已更新为遗失。', v_borrow_id;
ELSE
RAISE NOTICE '未找到该学生对此书的当前借阅记录,可能是在馆藏中发现遗失。';
END IF;
-- A2. 更新图书信息状态和可借数量 (如果这本书之前是可借的)
-- total_copies 一般不变,除非彻底报废且不再补充
UPDATE books
SET
status = 'lost', -- 如果整本书遗失
available_copies = GREATEST(0, available_copies - 1) -- 确保可借不为负
WHERE book_id = v_book_id AND status != 'lost'; -- 避免重复操作如果已是lost就不再减available_copies
-- A3. 生成罚款记录 (按书价赔偿)
INSERT INTO fines (student_id, amount, reason, status, issue_date, admin_id)
VALUES (v_student_id, v_book_price, '遗失图书《明朝那些事儿》(ISBN:9787559620187)', 'unpaid', current_date, v_admin_id);
RAISE NOTICE '遗失罚款已记录。';
-- 触发器 trg_freeze_account 会检查是否需要冻结账户
-- 触发器 trg_sync_book_student 会因为 borrow_records 更新而调整 current_borrow
END $$;
-- B. 处理图书损坏
-- 假设发现馆藏图书 (isbn='9787208159659', 三体全集) 有一本损坏,需要记录并可能罚款最后借阅人
DO $$
DECLARE
v_book_id bigint;
v_last_borrower_student_id bigint;
v_damage_fine_amount numeric := 20.00; -- 损坏赔偿金额
v_admin_id bigint := (SELECT admin_id FROM admins WHERE emp_no = 'A002');
BEGIN
SELECT book_id INTO v_book_id FROM books WHERE isbn = '9787208159659';
-- B1. 更新图书状态,如果损坏到不可借阅,减少可借数量
UPDATE books
SET
status = 'damaged',
available_copies = GREATEST(0, available_copies - 1) -- 如果损坏导致不可借
WHERE book_id = v_book_id;
RAISE NOTICE '图书 % 状态已更新为损坏。', v_book_id;
-- B2. (可选) 查找最后借阅人并生成罚款
SELECT student_id INTO v_last_borrower_student_id
FROM borrow_records
WHERE book_id = v_book_id AND return_date IS NOT NULL
ORDER BY return_date DESC
LIMIT 1;
IF FOUND THEN
INSERT INTO fines (student_id, amount, reason, status, issue_date, admin_id)
VALUES (v_last_borrower_student_id, v_damage_fine_amount, '损坏图书《三体全集》(ISBN:9787208159659)', 'unpaid', current_date, v_admin_id);
RAISE NOTICE '已向最后借阅人 (学生ID: %) 记录损坏罚款。', v_last_borrower_student_id;
-- 触发器 trg_freeze_account
ELSE
RAISE NOTICE '未找到该书的最后借阅人,或为馆藏期间损坏。';
END IF;
END $$;
-- C. 图书下架 (例如,图书过于陈旧或残破不再流通)
UPDATE books
SET status = 'removed', available_copies = 0
WHERE isbn = 'ISBN_TO_BE_REMOVED';
----------------------------------------------------------------------
-- (6). 设置和管理罚款规则 (主要通过 system_settings 表)
----------------------------------------------------------------------
-- A. 查看当前罚款规则
SELECT * FROM system_settings WHERE setting_key LIKE 'fine%';
-- B. 修改每日逾期罚款金额
UPDATE system_settings
SET setting_value = '0.30' -- 修改为每天0.3元
WHERE setting_key = 'fine_per_day';
-- C. 修改欠款冻结账户的阈值
UPDATE system_settings
SET setting_value = '15.00' -- 修改为欠款15元冻结
WHERE setting_key = 'freeze_threshold';
-- D. 添加新的罚款规则 (如果系统支持更复杂的规则,可能需要修改表结构或增加新表)
-- 当前设计下,罚款规则比较简单,主要就是每日费率。
----------------------------------------------------------------------
-- (7). 生成各类统计报表 (主要使用视图和聚合查询)
----------------------------------------------------------------------
-- A. 查看热门图书 (借阅量前20使用视图)
SELECT * FROM v_hot_books;
-- B. 查看各院系借阅统计 (使用视图)
SELECT * FROM v_dept_borrow_stats;
-- C. 查看图书逾期情况 (使用视图)
SELECT vd.*, b.title AS book_title, s.name AS student_name, s.phone AS student_phone
FROM v_overdue_details vd
JOIN books b ON vd.book_id = b.book_id
JOIN students s ON vd.student_id = s.student_id;
-- D. 自定义报表:某一时段内各类图书的借阅次数
SELECT
b.classification_no,
COUNT(br.borrow_id) AS borrow_count
FROM borrow_records br
JOIN books b ON br.book_id = b.book_id
WHERE br.borrow_date BETWEEN '2025-01-01' AND '2025-12-31' -- 示例时间段
GROUP BY b.classification_no
ORDER BY borrow_count DESC;
-- E. 自定义报表:每月借阅总量趋势
SELECT
to_char(borrow_date, 'YYYY-MM') AS borrow_month,
COUNT(borrow_id) AS total_borrows
FROM borrow_records
GROUP BY borrow_month
ORDER BY borrow_month;
-- F. 调用存储过程生成定期报表 (如果 sp_generate_circulation_stats 是设计为手动触发的)
-- SELECT sp_generate_circulation_stats();
-- 然后查询报表数据
-- SELECT * FROM circulation_stats ORDER BY stat_date DESC;
----------------------------------------------------------------------
-- (8). 系统参数设置 (主要通过 system_settings 表)
----------------------------------------------------------------------
-- A. 查看所有系统参数
SELECT * FROM system_settings;
-- B. 修改默认最大借阅量
UPDATE system_settings
SET setting_value = '8'
WHERE setting_key = 'max_borrow_default';
-- 注意:这只影响未来新建学生账户的默认值,或在 sp_init_students 中使用此值批量更新。
-- 已有学生的 max_borrow 需要单独修改,或在初始化脚本中覆盖。
-- C. 修改默认借阅期限(天数)
UPDATE system_settings
SET setting_value = '25'
WHERE setting_key = 'borrow_duration_days';
-- D. 修改最大续借次数
UPDATE system_settings
SET setting_value = '1'
WHERE setting_key = 'max_renew_times';
-- E. 修改预约保留天数
UPDATE system_settings
SET setting_value = '2'
WHERE setting_key = 'reservation_expiry_days';
-- F. 添加新的系统参数 (如果应用需要)
-- INSERT INTO system_settings (setting_key, setting_value)
-- VALUES ('new_feature_xyz_enabled', 'true');