2025-06-24 10:56:21 +08:00

340 lines
14 KiB
SQL
Raw Permalink 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.

-- 假设当前操作的学生是 张三 (学号 S2023001),我们先获取其 student_id
-- 在实际应用中,这个 ID 通常来自登录会话
DO $$
DECLARE
v_student_id bigint;
BEGIN
SELECT student_id INTO v_student_id FROM students WHERE stu_no = 'S2023001';
-- 将 v_student_id 设为会话级变量,方便后续查询使用
-- 注意: 这是一种在 psql 或特定SQL工具中设置变量的方式
-- 在应用代码中,你会直接使用从会话中获取的 student_id 变量。
EXECUTE 'SET app.current_student_id = ' || v_student_id;
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE NOTICE '学生 S2023001 未找到。请确保测试数据已插入。';
-- 或者你可以在这里直接硬编码一个存在的 student_id 用于测试
-- EXECUTE 'SET app.current_student_id = 1';
END $$;
-- 如果上面的 DO $$ 块由于环境限制无法执行 SET app.current_student_id
-- 后续查询中请手动替换 current_setting('app.current_student_id')::bigint 为实际的 student_id (例如 1)
-- (1). 多条件组合查询图书
RAISE NOTICE '---------- (1) 多条件组合查询图书 ----------';
-- 按书名模糊查询 (使用 pg_trgm GIN 索引)
RAISE NOTICE '-- 按书名模糊查询 "计算机":';
SELECT book_id, isbn, title, authors, available_copies
FROM books
WHERE title LIKE '%计算机%' AND status = 'normal';
RAISE NOTICE '-- 按书名精确查询 (如果知道全名):';
SELECT book_id, isbn, title, authors, available_copies
FROM books
WHERE title = '数学之美' AND status = 'normal';
-- 按作者查询 (使用 GIN 索引)
RAISE NOTICE '-- 按作者查询包含 "吴军":';
SELECT book_id, isbn, title, authors, available_copies
FROM books
WHERE authors @> ARRAY['吴军'] AND status = 'normal'; -- '@>' 表示数组包含
-- 按分类号查询
RAISE NOTICE '-- 按分类号查询 "TP301":';
SELECT book_id, isbn, title, authors, classification_no, available_copies
FROM books
WHERE classification_no = 'TP301' AND status = 'normal';
-- 按ISBN精确查询
RAISE NOTICE '-- 按ISBN查询 "9787111624927":';
SELECT book_id, isbn, title, authors, available_copies
FROM books
WHERE isbn = '9787111624927' AND status = 'normal';
-- 组合查询:书名包含 "编程" 且 作者包含 "Eric Matthes"
RAISE NOTICE '-- 组合查询:书名包含 "编程" 且 作者包含 "Eric Matthes":';
SELECT book_id, isbn, title, authors, available_copies
FROM books
WHERE title LIKE '%编程%' AND authors @> ARRAY['Eric Matthes'] AND status = 'normal';
-- (2). 查看图书详情及当前可借状态
RAISE NOTICE '---------- (2) 查看图书详情及当前可借状态 ----------';
-- 假设查看 ISBN 为 '9787111624927' (深入理解计算机系统) 的图书详情
-- 首先获取 book_id
DO $$
DECLARE
v_book_id bigint;
BEGIN
SELECT book_id INTO v_book_id FROM books WHERE isbn = '9787111624927';
EXECUTE 'SET app.current_book_id = ' || v_book_id;
END $$;
RAISE NOTICE '-- 查看图书 (ISBN: 9787111624927) 的详情:';
SELECT
book_id,
isbn,
title,
authors,
publisher,
publish_date,
price,
classification_no,
location,
total_copies,
available_copies,
status,
description,
cover_url,
CASE
WHEN status = 'normal' AND available_copies > 0 THEN '可借阅'
WHEN status = 'normal' AND available_copies = 0 THEN '已全部借出'
ELSE '不可借阅 (' || status::text || ')'
END as borrow_availability
FROM books
WHERE book_id = current_setting('app.current_book_id')::bigint;
-- (3). 在线预约热门图书(当所有副本都被借出时)
RAISE NOTICE '---------- (3) 在线预约热门图书 ----------';
-- 假设要预约的书是 'Java核心技术 卷I' (book_id=7),其 available_copies 在测试数据中为0
-- 首先确认该书确实没有可借副本
DO $$
DECLARE
v_target_book_id bigint := 7; -- Java核心技术 卷I
v_available int;
BEGIN
SELECT available_copies INTO v_available FROM books WHERE book_id = v_target_book_id AND status = 'normal';
IF v_available = 0 THEN
RAISE NOTICE '-- 图书ID % (Java核心技术 卷I) 当前无可用副本,可以进行预约。';
-- 学生张三 (student_id=1) 进行预约
-- 检查是否已预约过
IF NOT EXISTS (SELECT 1 FROM reservations
WHERE book_id = v_target_book_id
AND student_id = current_setting('app.current_student_id')::bigint
AND status IN ('waiting', 'available'))
THEN
INSERT INTO reservations (book_id, student_id, reserve_date, status)
VALUES (v_target_book_id, current_setting('app.current_student_id')::bigint, current_date, 'waiting');
RAISE NOTICE '-- 学生ID % 已成功预约图书ID %。', current_setting('app.current_student_id')::bigint, v_target_book_id;
ELSE
RAISE NOTICE '-- 学生ID % 已预约过图书ID % 或预约已可取。', current_setting('app.current_student_id')::bigint, v_target_book_id;
END IF;
ELSE
RAISE NOTICE '-- 图书ID % 当前有 % 本可借,无需预约。', v_target_book_id, v_available;
END IF;
END $$;
-- 查看张三的预约记录来确认
RAISE NOTICE '-- 查看学生张三的当前预约:';
SELECT r.reservation_id, b.title, r.reserve_date, r.status
FROM reservations r
JOIN books b ON r.book_id = b.book_id
WHERE r.student_id = current_setting('app.current_student_id')::bigint AND r.status IN ('waiting', 'available');
-- (4). 查看个人借阅历史、当前借阅情况和预约状态
RAISE NOTICE '---------- (4) 查看个人借阅历史、当前借阅情况和预约状态 ----------';
RAISE NOTICE '-- 学生张三 (ID: %) 的当前借阅情况:';
SELECT br.borrow_id, b.title, b.isbn, br.borrow_date, br.due_date, br.renew_times, br.status
FROM borrow_records br
JOIN books b ON br.book_id = b.book_id
WHERE br.student_id = current_setting('app.current_student_id')::bigint
AND br.status IN ('borrowed', 'overdue');
RAISE NOTICE '-- 学生张三 (ID: %) 的借阅历史 (已还):';
SELECT br.borrow_id, b.title, b.isbn, br.borrow_date, br.return_date, br.status
FROM borrow_records br
JOIN books b ON br.book_id = b.book_id
WHERE br.student_id = current_setting('app.current_student_id')::bigint
AND br.status = 'returned';
RAISE NOTICE '-- 学生张三 (ID: %) 的当前预约状态:'; -- 重复上面的预约查询,为了完整性
SELECT r.reservation_id, b.title, r.reserve_date, r.status
FROM reservations r
JOIN books b ON r.book_id = b.book_id
WHERE r.student_id = current_setting('app.current_student_id')::bigint AND r.status IN ('waiting', 'available');
-- (5). 在线续借图书(有限制条件)
RAISE NOTICE '---------- (5) 在线续借图书 ----------';
-- 假设张三要续借他借阅的《深入理解计算机系统》(book_id=1)
-- borrow_id 需要从他的当前借阅中找到,假设其 borrow_id 为 1 (根据你的测试数据)
DO $$
DECLARE
v_borrow_id_to_renew bigint;
v_student_id bigint := current_setting('app.current_student_id')::bigint;
v_book_id_of_borrow bigint;
v_max_renew_times int;
v_borrow_duration_days int;
v_current_renew_times int;
v_current_due_date date;
v_current_status borrow_status_enum;
v_is_reserved boolean;
BEGIN
-- 找到张三正在借阅的《深入理解计算机系统》的 borrow_id
SELECT br.borrow_id, br.book_id, br.renew_times, br.due_date, br.status
INTO v_borrow_id_to_renew, v_book_id_of_borrow, v_current_renew_times, v_current_due_date, v_current_status
FROM borrow_records br
JOIN books b ON br.book_id = b.book_id
WHERE br.student_id = v_student_id
AND b.isbn = '9787111624927' -- 《深入理解计算机系统》
AND br.status = 'borrowed'
LIMIT 1;
IF NOT FOUND THEN
RAISE NOTICE '-- 未找到学生ID % 对图书ISBN 9787111624927 的可续借记录。';
RETURN;
END IF;
RAISE NOTICE '-- 尝试为学生ID % 续借 borrow_id % (Book ID: %)', v_student_id, v_borrow_id_to_renew, v_book_id_of_borrow;
-- 获取系统参数
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_days FROM system_settings WHERE setting_key = 'borrow_duration_days';
-- 检查条件:
-- 1. 状态为 'borrowed' (不能是 'overdue' 或 'lost')
IF v_current_status != 'borrowed' THEN
RAISE NOTICE '-- 续借失败: 图书状态为 %,不是 "borrowed"。', v_current_status;
RETURN;
END IF;
-- 2. 续借次数未达上限
IF v_current_renew_times >= v_max_renew_times THEN
RAISE NOTICE '-- 续借失败: 已达到最大续借次数 (%)。', v_max_renew_times;
RETURN;
END IF;
-- 3. 该书没有其他人预约
SELECT EXISTS (SELECT 1 FROM reservations WHERE book_id = v_book_id_of_borrow AND status = 'waiting')
INTO v_is_reserved;
IF v_is_reserved THEN
RAISE NOTICE '-- 续借失败: 该图书已被其他用户预约。';
RETURN;
END IF;
-- 执行续借
UPDATE borrow_records
SET due_date = v_current_due_date + (v_borrow_duration_days || ' days')::interval, -- 从原应还日期开始计算
renew_times = renew_times + 1
WHERE borrow_id = v_borrow_id_to_renew;
RAISE NOTICE '-- 续借成功! borrow_id: %, 新的应还日期: %, 当前续借次数: %',
v_borrow_id_to_renew,
(SELECT due_date FROM borrow_records WHERE borrow_id = v_borrow_id_to_renew),
(SELECT renew_times FROM borrow_records WHERE borrow_id = v_borrow_id_to_renew);
EXCEPTION
WHEN NO_DATA_FOUND THEN
RAISE NOTICE '-- 未找到相关借阅记录或系统参数。';
WHEN OTHERS THEN
RAISE NOTICE '续借操作发生错误: %', SQLERRM;
END $$;
-- (6). 查看和缴纳罚款
RAISE NOTICE '---------- (6) 查看和缴纳罚款 ----------';
-- 假设学生王五 (S2022003, student_id=3) 有未缴罚款
DO $$
DECLARE
v_student_id_wangwu bigint;
v_fine_id_to_pay bigint;
v_amount_to_pay numeric;
BEGIN
SELECT student_id INTO v_student_id_wangwu FROM students WHERE stu_no = 'S2022003';
IF NOT FOUND THEN
RAISE NOTICE '学生 S2022003 未找到。';
RETURN;
END IF;
RAISE NOTICE '-- 查看学生王五 (ID: %) 的未缴罚款:';
SELECT fine_id, amount, reason, status, issue_date
FROM fines
WHERE student_id = v_student_id_wangwu AND status = 'unpaid';
-- 假设王五要缴纳 fine_id 为 (从上面查询结果中选取一个,例如第一个未缴罚款)
SELECT fine_id, amount INTO v_fine_id_to_pay, v_amount_to_pay
FROM fines
WHERE student_id = v_student_id_wangwu AND status = 'unpaid'
ORDER BY issue_date
LIMIT 1;
IF v_fine_id_to_pay IS NOT NULL THEN
RAISE NOTICE '-- 模拟缴纳学生王五 (ID: %) 的罚款 fine_id: %, 金额: %', v_student_id_wangwu, v_fine_id_to_pay, v_amount_to_pay;
UPDATE fines
SET status = 'paid'
WHERE fine_id = v_fine_id_to_pay;
-- 触发器 trg_freeze_account 会在此 UPDATE 后执行,可能解冻账户
RAISE NOTICE '-- 缴纳后,再次查看学生王五 (ID: %) 的未缴罚款:';
SELECT fine_id, amount, reason, status, issue_date
FROM fines
WHERE student_id = v_student_id_wangwu AND status = 'unpaid';
RAISE NOTICE '-- 查看学生王五 (ID: %) 的账户状态 (可能已因罚款缴纳而解冻):';
SELECT stu_no, name, account_status FROM students WHERE student_id = v_student_id_wangwu;
ELSE
RAISE NOTICE '-- 学生王五 (ID: %) 没有未缴罚款可供缴纳。';
END IF;
END $$;
-- (7). 对已借阅图书进行评分和评论
RAISE NOTICE '---------- (7) 对已借阅图书进行评分和评论 ----------';
-- 假设张三 (student_id=1) 要对他借阅过的《深入理解计算机系统》(book_id=1)进行评价
-- (假设他已经借过或者正在借阅)
DO $$
DECLARE
v_student_id bigint := current_setting('app.current_student_id')::bigint;
v_book_id_to_review bigint;
BEGIN
SELECT book_id INTO v_book_id_to_review FROM books WHERE isbn = '9787111624927'; -- 深入理解计算机系统
-- 检查是否已经评论过 (因为有 UNIQUE(book_id, student_id) 约束)
IF EXISTS (SELECT 1 FROM reviews WHERE student_id = v_student_id AND book_id = v_book_id_to_review) THEN
RAISE NOTICE '-- 学生ID % 已评价过图书ID %。';
UPDATE reviews
SET rating = 5, content = '更新评价:太经典了,常看常新!', review_time = now()
WHERE student_id = v_student_id AND book_id = v_book_id_to_review;
RAISE NOTICE '-- 已更新学生ID % 对图书ID % 的评价。';
ELSE
-- 应用层面通常会检查该学生是否实际借阅过这本书,这里我们直接插入
INSERT INTO reviews (book_id, student_id, rating, content, review_time)
VALUES (v_book_id_to_review, v_student_id, 5, '这本书写得太好了,深入浅出,对我帮助很大!', now());
RAISE NOTICE '-- 学生ID % 已成功评价图书ID %。';
END IF;
RAISE NOTICE '-- 查看图书ID % 的所有评价:';
SELECT s.name as student_name, r.rating, r.content, r.review_time
FROM reviews r
JOIN students s ON r.student_id = s.student_id
WHERE r.book_id = v_book_id_to_review
ORDER BY r.review_time DESC;
END $$;
-- (8). 查看图书推荐(如基于热门被借阅图书)
RAISE NOTICE '---------- (8) 查看图书推荐(热门图书) ----------';
RAISE NOTICE '-- 查看当前热门图书 (基于 v_hot_books 视图):';
SELECT b.isbn, b.title, b.authors, vhb.borrow_cnt
FROM v_hot_books vhb
JOIN books b ON vhb.book_id = b.book_id
ORDER BY vhb.borrow_cnt DESC;
-- 另一种推荐思路:基于用户所在院系的热门图书
RAISE NOTICE '-- 查看学生张三所在院系的热门图书 (示例):';
WITH student_department AS (
SELECT department FROM students WHERE student_id = current_setting('app.current_student_id')::bigint
),
department_borrows AS (
SELECT
br.book_id,
COUNT(*) as dept_borrow_count
FROM borrow_records br
JOIN students s ON br.student_id = s.student_id
WHERE s.department = (SELECT department FROM student_department)
GROUP BY br.book_id
)
SELECT b.title, b.authors, db.dept_borrow_count
FROM department_borrows db
JOIN books b ON db.book_id = b.book_id
ORDER BY db.dept_borrow_count DESC
LIMIT 10;
RAISE NOTICE '---------- 学生功能SQL示例演示完毕 ----------';