340 lines
14 KiB
SQL
340 lines
14 KiB
SQL
-- 假设当前操作的学生是 张三 (学号 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示例演示完毕 ----------'; |