
簡介面向數據庫課程設計學習者這份某高校學生選課系統的設計資料包以學生選課場景為載體完整呈現數據庫課程設計從需求分析、概念結構設計到邏輯結構設計與物理實現的典型過程適合需要完成相似課設或鞏固數據庫原理的本科及高職學生參考。壓縮包內共3個文件其中Word版課程設計報告doc詳述系統分析、ER圖、關系模式及設計思路SQL腳本sql提供建庫建表與基礎數據數據庫備份bak便于直接還原查看運行效果整體僅802KB輕量易用。資源已有998人學習下載口碑較好屬于高分數課設。通過該資源讀者可學到選課系統涉及的學生、課程、成績等核心實體建模方法掌握數據庫定義、完整性約束設置與SQL編程技巧并借鑒規范化報告寫作框架為獨立完成課程設計提供有力支撐。1. 學生選課系統課程設計從一張成績單反推表結構拿到“某高校學生選課系統的設計”這個數據庫課程設計題目大多數人第一反應是建三張表、寫幾個增刪改查頁面。但真正拉開差距的不在“能不能跑”而在“跑起來之后還能不能守住業務規則”同一門課只剩一個名額時并發選課會不會超員、退課后成績記錄能不能追溯、學生能不能查到同一學期的課程沖突。這篇博文按做課程設計最常見的 MySQL 方案從 ER 模型、三張核心表、存儲過程、觸發器一路走到事務、權限和答辯演示驗證把一套可以完整復現的路線講清楚。適合拿了這題想認真做完而不是臨交差前復制粘貼的同學。2. 學生選課系統的數據庫設計ER模型、范式與核心表結構2.1 從需求描述到實體-聯系圖選課業務里必須畫清楚的三個實體寫這個課程設計我一般會先讓學生在紙上畫 ER 圖而不是直接打開 MySQL。學生、課程、選課記錄三個實體是骨架可選實體還有院系、教師、教室。最容易畫錯的地方是選課記錄它不是學生和課程之間的純粹連接表它自己就是實體承擔成績、選課時間、退課狀態這些屬性。評分看數據模型的人第一眼就看你有沒有把選課記錄當作實體對待。畫完實體還要標函數依賴。學號決定姓名、性別、學院課程號決定課程名、學分、教師、容量(學號, 課程號)決定選課時間和成績。凡是不完全依賴主鍵、或者存在傳遞依賴的字段都要拆出去。比如教師職稱和教師所屬院系如果在課程表里就會產生傳遞依賴要拆成教師表課程表只保留教師 ID。這也是數據庫面試題里反復問的“范式”在這個題目里最實在的落點。2.2 第三范式下的核心表結構學生、課程、選課三大表的字段取舍下面這套結構是我做這個課程設計時最常用的一版以 MySQL 8.0 為基準語法上也兼容很多其他數據庫。學生表studentstu_id CHAR(10) PK學號用定長字符而不是 INT學號是業務編號不是數值不參與數學運算stu_name VARCHAR(20) NOT NULLgender ENUM(M,F)major VARCHAR(50)專業grade SMALLINT UNSIGNED年級比如 2024enroll_date DATE入學時間課程表coursecourse_id CHAR(6) PKcourse_name VARCHAR(50) NOT NULLcredit DECIMAL(2,1)學分支持 3.5 這種小數teacher_id CHAR(6)教師編號關聯教師表capacity SMALLINT UNSIGNED DEFAULT 30課程容量selected_count SMALLINT UNSIGNED DEFAULT 0已選人數selected_count是冗余字段它能避免每次選課都去COUNT(*)全表掃一遍但冗余字段必須在寫入時同步維護否則會出現不一致。這個矛盾后面用觸發器兜底。選課表enrollment的字段設計字段類型說明stu_idCHAR(10)學號復合主鍵之一course_idCHAR(6)課程號復合主鍵之一enroll_timeDATETIME選課時間默認當前時間gradeDECIMAL(4,1)成績允許為空statusENUM(selected,dropped)選課狀態退課后保留記錄status字段是很多人會漏掉的設計退課不應該物理刪除選課記錄否則成績數據、選課歷史全沒了。用狀態標記才能回答“這學生退過哪些課”這類問題。2.3 建庫建表 SQL 完整腳本課程設計第一版可執行代碼CREATE DATABASE IF NOT EXISTS course_db DEFAULT CHARSET utf8mb4; USE course_db; CREATE TABLE student ( stu_id CHAR(10) NOT NULL, stu_name VARCHAR(20) NOT NULL, gender ENUM(M, F) DEFAULT M, major VARCHAR(50) NOT NULL, grade SMALLINT UNSIGNED, enroll_date DATE, PRIMARY KEY (stu_id) ); CREATE TABLE course ( course_id CHAR(6) NOT NULL, course_name VARCHAR(50) NOT NULL, credit DECIMAL(2,1) DEFAULT 2.0, teacher_id CHAR(6), capacity SMALLINT UNSIGNED DEFAULT 30, selected_count SMALLINT UNSIGNED DEFAULT 0, PRIMARY KEY (course_id) ); CREATE TABLE enrollment ( stu_id CHAR(10) NOT NULL, course_id CHAR(6) NOT NULL, enroll_time DATETIME DEFAULT CURRENT_TIMESTAMP, grade DECIMAL(4,1) DEFAULT NULL, status ENUM(selected,dropped) DEFAULT selected, PRIMARY KEY (stu_id, course_id), CONSTRAINT fk_enr_stu FOREIGN KEY (stu_id) REFERENCES student(stu_id) ON DELETE CASCADE, CONSTRAINT fk_enr_cou FOREIGN KEY (course_id) REFERENCES course(course_id) );邏輯說明刪除學生時級聯刪除其選課記錄刪除課程時不做級聯用約束默認的RESTRICT行為阻止刪除已經被選過的課程保護歷史數據。字符集統一utf8mb4避免中文亂碼和 emoji 導致的存儲問題。capacity和selected_count用SMALLINT UNSIGNED就足夠課程容量不可能超過 65535沒必要給INT。3. 用存儲過程與觸發器實現學生選課系統的核心業務選課、退課、查余量3.1 選課存儲過程把“查余量、判重復、寫選課記錄、扣名額”裝進一個事務選課的核心邏輯是四個動作的串行組合。如果讓應用層分四步執行任何一步失敗都會留下半個業務狀態。常見做法是寫一個存儲過程把所有動作放進一個事務邊界。DELIMITER $$ CREATE PROCEDURE sp_enroll_course( IN p_stu_id CHAR(10), IN p_course_id CHAR(6) ) BEGIN DECLARE v_capacity INT; DECLARE v_selected INT; START TRANSACTION; SELECT capacity, selected_count INTO v_capacity, v_selected FROM course WHERE course_id p_course_id FOR UPDATE; IF v_selected v_capacity THEN ROLLBACK; SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT 課程人數已滿; END IF; IF EXISTS (SELECT 1 FROM enrollment WHERE stu_id p_stu_id AND course_id p_course_id AND status selected) THEN ROLLBACK; SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT 不能重復選課; END IF; INSERT INTO enrollment(stu_id, course_id) VALUES (p_stu_id, p_course_id); UPDATE course SET selected_count selected_count 1 WHERE course_id p_course_id; COMMIT; END$$ DELIMITER ;邏輯說明FOR UPDATE是行級排他鎖鎖住課程表的這一行讓兩個并發會話不能同時讀到同一個“剩余名額”。SIGNAL語句主動拋出異常并攜帶中文錯誤信息應用層直接捕獲異常消息就能知道失敗原因比返回一個錯誤碼讓前端猜更穩。參數說明p_stu_id和p_course_id是輸入參數分別對應學生學號和課程號。這個存儲過程沒有輸出參數業務是否成功通過異常判斷調用方需要把調用包在try-catch里處理異常消息。3.2 觸發器兜底直接 INSERT 也超不了選的容量保護存儲過程能攔住按規矩走接口的人攔不住繞過存儲過程直接執行INSERT INTO enrollment的操作。課程設計里經常出現這樣的場景管理系統后臺有一個“手動補錄選課”的功能開發時圖省事直接寫了一條 INSERT結果容量限制完全失效。觸發器能把這道防線沉到數據庫引擎層。CREATE TRIGGER trg_enroll_before_insert BEFORE INSERT ON enrollment FOR EACH ROW BEGIN DECLARE v_selected INT; DECLARE v_capacity INT; SELECT selected_count, capacity INTO v_selected, v_capacity FROM course WHERE course_id NEW.course_id FOR UPDATE; IF v_selected v_capacity THEN SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT 選課人數已滿觸發器攔截; END IF; UPDATE course SET selected_count selected_count 1 WHERE course_id NEW.course_id; END邏輯說明NEW.course_id引用的是即將插入選課表的課程號。觸發器先讀課程人數超員就拋異常阻止插入未超員就自動維護selected_count。這樣手動 INSERT 也被納入容量控制。要注意的是普通 MySQL 觸發器默認基于BEFORE或AFTER操作在BEFORE里拋異常對應的 INSERT 會整體失敗。這里沒有寫FOR EACH ROW之外的分區條件FOR EACH ROW本身是 MySQL 觸發器的固定語法每行操作都會執行。3.3 視圖與索引三條高頻查詢路徑的數據庫 SQL 優化課程設計答辯時老師最喜歡問“你做了哪些查詢優化”。至少有三條高頻 SQL 需要保證性能學生查自己已選課程及成績、學生查課程余量、教師查某門課選課名單。對應關系可以用一張表說清查詢場景涉及表應命中的索引學生查成績單enrollment course studentenrollment 復合主鍵(stu_id, course_id)查課程余量coursecourse 主鍵course_id教師查選課名單enrollmentenrollment 的course_id前綴索引enrollment的復合主鍵是(stu_id, course_id)按學生查成績單時走的是聯合索引最左前綴按課程查名單則不行需要額外建一個索引ALTER TABLE enrollment ADD INDEX idx_enr_course (course_id); CREATE VIEW v_student_score AS SELECT s.stu_id, s.stu_name, c.course_name, e.grade FROM enrollment e JOIN student s ON e.stu_id s.stu_id JOIN course c ON e.course_id c.course_id WHERE e.status selected; CREATE VIEW v_course_remain AS SELECT course_id, course_name, capacity - selected_count AS remain FROM course;邏輯說明視圖不存儲數據每次查詢都會展開成底層 SQL 執行 JOIN。v_student_score把成績單查詢封裝成語義清晰的接口應用層不必拼復雜的 JOIN。v_course_remain里capacity - selected_count是計算列直接暴露余量應用層把remain 0作為可選的判斷條件。3.4 課程設計演示數據的最小增刪改查集造數據不要手寫上百條 INSERT先準備一個最小集合夠展示功能就行INSERT INTO student VALUES (20240001, 趙一, M, 計算機學院, 2024, 2024-09-01), (20240002, 錢二, F, 軟件學院, 2024, 2024-09-01), (20240003, 孫三, M, 計算機學院, 2024, 2024-09-01); INSERT INTO course VALUES (CS101, 數據庫原理, 3.0, T001, 2, 0), (CS102, 操作系統, 3.5, T002, 30, 0); CALL sp_enroll_course(20240001, CS101); CALL sp_enroll_course(20240002, CS101); CALL sp_enroll_course(20240003, CS101);這里把CS101的容量設成 2第三個學生選課會觸發存儲過程里的滿員異常演示效果最直觀。三個學生兩門課足夠覆蓋查成績、查余量、滿員選課失敗、退課四個必演示場景。4. 從課程設計到答辯演示事務、權限與數據一致性驗證4.1 并發超選的經典翻車現場為什么不能只寫一條 INSERT如果課程設計里選課功能只有一句INSERT INTO enrollment演示時瀏覽器開兩個窗口同時點選課就能復現數據不一致。MySQL 客戶端開兩個會話模擬兩個學生選同一門只剩一個名額的課-- 會話 A START TRANSACTION; UPDATE course SET selected_count selected_count 1 WHERE course_id CS101; -- 會話 B在 A 提交前執行 UPDATE course SET selected_count selected_count 1 WHERE course_id CS101;會話 B 的 UPDATE 會阻塞等 A 提交后 B 才繼續。這個過程中B 在不知道 A 已占名額的情況下可能已經向應用層返回了“選課中”的中間狀態寫入選課記錄時又產生重復。正確做法是把“查余量、插選課記錄、更新人數”放進同一個事務并且在一開始就SELECT ... FOR UPDATE鎖住課程行。第三章的存儲過程已經把這一步做好了這里重點是用兩個會話把效果跑出來給答辯老師看。4.2 MySQL 用戶與權限設計一套庫同時服務學生、教師、管理員課程設計規范里要求“不同角色不同權限”這句話要落到數據庫用戶層面才有說服力。MySQL 里可以建兩個用戶對應學生和管理員兩個角色CREATE USER stu_userlocalhost IDENTIFIED BY Stu2024; CREATE USER admin_userlocalhost IDENTIFIED BY Admin2024; GRANT SELECT, INSERT, UPDATE ON course_db.enrollment TO stu_userlocalhost; GRANT SELECT ON course_db.course TO stu_userlocalhost; GRANT ALL PRIVILEGES ON course_db.* TO admin_userlocalhost; FLUSH PRIVILEGES;權限矩陣可以整理成表格寫進設計報告角色可操作表權限范圍學生enrollmentSELECT / INSERT / UPDATE僅自己的記錄學生courseSELECT管理員全部表所有權限教師enrollment / courseSELECT擴展場景“學生只能改自己的記錄”在 MySQL 單靠表級授權不夠GRANT無法指定行級條件。實際項目中通常通過視圖加WHERE stu_id 當前登錄用戶實現行級過濾或者在應用層校驗。課程設計里把賬權限矩陣寫清楚比硬編碼實現更專業。4.3 事務回滾驗證腳本演示用一段異常 SQL 證明原子性答辯現場只展示“正常選課成功”沒有沖擊力要主動表演一次回滾START TRANSACTION; INSERT INTO enrollment(stu_id, course_id) VALUES (20240001, CS102); UPDATE course SET selected_count selected_count 1 WHERE course_id CS102; -- 故意制造一個錯誤不存在的課程 INSERT INTO enrollment(stu_id, course_id) VALUES (20240001, XX999); ROLLBACK;執行到這個故意寫錯的課程XX999時外鍵約束會拒絕插入事務進入異常狀態。如果不做ROLLBACK前面的選課記錄雖然已經寫入內存中的事務日志但不會真正落盤。執行完 ROLLBACK 后SELECT * FROM enrollment看不到CS102那條記錄selected_count也沒有變化這就是原子性的直觀演示。需要補充的是MySQL 客戶端默認autocommit1上面用顯式START TRANSACTION手動關閉了自動提交所以 ROLLBACK 才有意義。如果不開事務直接跑三條 INSERT失敗的那條會擋住后續語句但前面成功的語句已經永久提交清理起來更麻煩。5. 數據庫課程設計答辯前的 5 個驗證動作5.1 用 EXPI 證明索引真實生效不要只說“我建了索引”現場跑EXPLAIN SELECT * FROM enrollment WHERE course_id CS101;看執行計劃里的key字段是不是idx_enr_coursetype是不是ref。如果key是 NULL說明索引沒建上或者查詢沒走索引先ALTER TABLE enrollment ADD INDEX idx_enr_course(course_id);再跑一遍。5.2 用異常回滾證明事務不是擺設按 4.3 的腳本執行一次把回滾前后的SELECT COUNT(*) FROM enrollment結果截圖對比。評審老師看到“異常后數據恢復原狀”比什么口頭說明都有效。5.3 用 mysqldump 命令準備一鍵恢復mysqldump -u root -p course_db course_db_backup.sql mysql -u root -p course_db course_db_backup.sql演示時先把庫里的數據刪兩條再執行導入命令還原。注意恢復前要確認目標庫存在否則執行導入會直接報錯Unknown database。5.4 留一個擴展點選課日志表與連接池參數有余力的話加一張enrollment_log表記錄每次選課/退課操作的客戶端 IP 和操作時間用觸發器寫入。這是課程設計評分里很加分的擴展功能同時也是軟件工程課程設計里審計模塊的雛形。如果項目對接 Java 后端把連接池參數寫進application.yml的 HikariCP 配置里maximum-pool-size調成 5 到 10演示時不會因為連接數爆炸卡死。答辯前把這五個動作依次過一遍每個結論都有 SQL 輸出撐腰本數據庫課程設計的數據側支撐就完整了。本文還有配套的精品資源點擊獲取