管理系統(tǒng)數(shù)據(jù)庫(kù)課程設(shè)計(jì):從E-R圖到建表SQL的避坑指南)
簡(jiǎn)介一份理工學(xué)院的數(shù)據(jù)庫(kù)課程設(shè)計(jì)報(bào)告——教務(wù)管理系統(tǒng)采用C#等面向?qū)ο笳Z(yǔ)言與關(guān)系數(shù)據(jù)庫(kù)技術(shù)完成適合計(jì)算機(jī)科學(xué)與技術(shù)專(zhuān)業(yè)學(xué)生參考課程設(shè)計(jì)的寫(xiě)作結(jié)構(gòu)、數(shù)據(jù)庫(kù)建模思路及系統(tǒng)開(kāi)發(fā)流程。報(bào)告覆蓋需求分析、可行性分析、ER模型設(shè)計(jì)、系統(tǒng)功能模塊劃分、界面展示、設(shè)計(jì)總結(jié)與開(kāi)發(fā)體會(huì)等完整章節(jié)并將教務(wù)員、教師、學(xué)生、系統(tǒng)管理員四類(lèi)用戶(hù)的權(quán)限控制、成績(jī)管理、自動(dòng)排課等業(yè)務(wù)流程梳理得較為清晰。資源為單個(gè)doc文檔共287KB內(nèi)容詳實(shí)、結(jié)構(gòu)規(guī)范可直接對(duì)照撰寫(xiě)同類(lèi)課程設(shè)計(jì)報(bào)告或提取教務(wù)場(chǎng)景下的實(shí)體關(guān)系、表結(jié)構(gòu)設(shè)計(jì)以及C#與數(shù)據(jù)庫(kù)交互的編程思路。目前已有96人學(xué)習(xí)下載對(duì)正在開(kāi)展數(shù)據(jù)庫(kù)課程設(shè)計(jì)的高校學(xué)生具備實(shí)操參考價(jià)值。1. 教務(wù)管理系統(tǒng)課程設(shè)計(jì)報(bào)告.doc這份文檔在評(píng)的是什么能力如果你正在做數(shù)據(jù)庫(kù)課程設(shè)計(jì)多半被要求交一份后綴是 .doc 的“課程設(shè)計(jì)報(bào)告”題目里寫(xiě)著“教務(wù)管理系統(tǒng)”。很多同學(xué)第一反應(yīng)是去網(wǎng)上找個(gè)模板把表結(jié)構(gòu)抄一抄、界面截圖貼幾張然后祈禱評(píng)審老師不要仔細(xì)看。結(jié)果往往是答辯時(shí)被一句“你這張表為什么這樣設(shè)計(jì)”問(wèn)住整個(gè)報(bào)告從頭翻到尾也找不到依據(jù)。這個(gè)標(biāo)題背后真正要練的能力不是寫(xiě)代碼畫(huà)界面而是“從業(yè)務(wù)規(guī)則推導(dǎo)出表結(jié)構(gòu)再把推導(dǎo)過(guò)程寫(xiě)成能復(fù)核的文檔”這一整條鏈路。我能給的確定判斷是這份文檔評(píng)審時(shí)老師先看的是 E-R 圖、范式級(jí)別、主外鍵約束和數(shù)據(jù)字典最后才看實(shí)現(xiàn)了多少功能。如果你把順序搞反了先寫(xiě)頁(yè)面再回頭補(bǔ)表那文檔基本會(huì)寫(xiě)成一本“事后回憶錄”邏輯上是斷裂的。下面整個(gè)方案會(huì)按教務(wù)管理系統(tǒng)最常見(jiàn)的需求從需求分析、E-R 設(shè)計(jì)、邏輯結(jié)構(gòu)、物理實(shí)現(xiàn)到報(bào)告成稿一步步拆開(kāi)講順帶把評(píng)審最?lèi)?ài)挑的坑提前排掉。2. 設(shè)計(jì)是從需求到 E-R 圖的約束推導(dǎo)這個(gè)階段決定了報(bào)告的上限很多新人把 E-R 圖當(dāng)作“畫(huà)個(gè)矩形和菱形交差”的環(huán)節(jié)這是最大誤判。E-R 圖在數(shù)據(jù)庫(kù)課程設(shè)計(jì)報(bào)告里承擔(dān)的是“需求的可視化證據(jù)”評(píng)審老師能一眼看出你是否真的理解業(yè)務(wù)。所以這個(gè)階段的核心不是畫(huà)圖技巧而是“哪些實(shí)體、哪些聯(lián)系、哪些屬性是必須要有的”。2.1 實(shí)體識(shí)別從數(shù)據(jù)的最小單元拆起教務(wù)管理系統(tǒng)的常見(jiàn)實(shí)體清單幾乎是固定的學(xué)生、教師、院系或?qū)I(yè)、課程、班級(jí)、選課記錄、成績(jī)記錄。不要在這個(gè)基礎(chǔ)上擅自加“管理員”這種沒(méi)有任何屬性細(xì)節(jié)的實(shí)體它會(huì)顯得你是在湊數(shù)。每個(gè)實(shí)體必須問(wèn)自己三個(gè)問(wèn)題這個(gè)實(shí)體的實(shí)例是什么用哪個(gè)屬性能唯一標(biāo)識(shí)它它在系統(tǒng)里產(chǎn)生過(guò)什么數(shù)據(jù)比如“院系”和“專(zhuān)業(yè)”常常被拆開(kāi)理由是專(zhuān)業(yè)歸屬于院系而且你后續(xù)設(shè)計(jì)“學(xué)生”表時(shí)可以通過(guò)專(zhuān)業(yè)關(guān)聯(lián)到院系避免學(xué)生表里同時(shí)出現(xiàn)兩個(gè)冗余的院系字段。課程設(shè)計(jì)如果只有三個(gè)院系你可能會(huì)覺(jué)得沒(méi)必要拆成兩張表但從設(shè)計(jì)規(guī)范性上說(shuō)拆開(kāi)更好寫(xiě)三類(lèi)文檔內(nèi)容E-R 分層圖、關(guān)系模式說(shuō)明、數(shù)據(jù)字典都能多一層層級(jí)關(guān)系。我一般會(huì)把學(xué)生、課程、教師、院系、選課記錄這五類(lèi)作為必需項(xiàng)把班級(jí)和教師授課作為加分項(xiàng)。2.2 屬性歸屬判斷“它貼在誰(shuí)身上”屬性的歸屬是這門(mén)課的第一個(gè)分水嶺。常見(jiàn)錯(cuò)誤是把“課程名稱(chēng)”“課程學(xué)分”“任課教師”全堆在“課程”一個(gè)實(shí)體上。這看起來(lái)方便但會(huì)在后續(xù)邏輯設(shè)計(jì)時(shí)產(chǎn)生函數(shù)依賴(lài)問(wèn)題。任課教師不應(yīng)該是課程實(shí)體的直接屬性因?yàn)橐婚T(mén)課程可以被多位教師在不同學(xué)期開(kāi)設(shè)所以“教師”應(yīng)該獨(dú)立成實(shí)體與課程通過(guò)“教師授課”聯(lián)系——如果你希望報(bào)告更接近真實(shí)教務(wù)系統(tǒng)還需要上課期、上課時(shí)間這些屬性那這個(gè)就變成了“授課安排”實(shí)體E-R 圖里會(huì)多一個(gè)菱形反而說(shuō)明你思考得更細(xì)。另一個(gè)判斷規(guī)則是如果一個(gè)屬性的取值會(huì)根據(jù)另外兩個(gè)實(shí)體的組合才確定那它就不屬于任何單一實(shí)體屬于“聯(lián)系上的屬性”。最典型的就是“成績(jī)”——它依賴(lài)“學(xué)生課程”這個(gè)組合存在單獨(dú)掛在學(xué)生或課程上都會(huì)造成冗余。選課記錄里的“選課時(shí)間”也是一個(gè)聯(lián)系屬性。報(bào)告里只要出現(xiàn)這類(lèi)屬性評(píng)審就會(huì)確認(rèn)你是認(rèn)真做過(guò)需求分析的。2.3 聯(lián)系的類(lèi)型和翻譯規(guī)則E-R 圖里最讓新手頭疼的是判斷 1:1、1:N、M:N。這里給一個(gè)不燒腦的判斷方法站在“一個(gè)”的角度問(wèn)一個(gè) A 能對(duì)應(yīng)幾個(gè) B一個(gè) B 能對(duì)應(yīng)幾個(gè) A兩邊的答案合在一起就是聯(lián)系類(lèi)型。以學(xué)生和課程為例一個(gè)學(xué)生能選多門(mén)課一門(mén)課也能被多個(gè)學(xué)生選那就是 M:N。學(xué)生和院系則是一個(gè)院系有多個(gè)學(xué)生一個(gè)學(xué)生只屬于一個(gè)院系也就是 N:1。M:N 聯(lián)系在關(guān)系模型中不能直接做成表字段必須拆出一個(gè)中間表。這就是“選課表”的由來(lái)它的主鍵是學(xué)號(hào)課程號(hào)聯(lián)合主鍵。E-R 圖轉(zhuǎn)關(guān)系模式的規(guī)則我一般這樣寫(xiě)進(jìn)報(bào)告1:N 聯(lián)系把“1”端的主鍵放入“N”端作為外鍵M:N 聯(lián)系獨(dú)立建表兩端主鍵都拿進(jìn)來(lái)共同組成聯(lián)合主鍵1:1 聯(lián)系少見(jiàn)通常直接合并到任一端。這段規(guī)則本身也要寫(xiě)進(jìn)報(bào)告因?yàn)樵u(píng)審要看到你“會(huì)翻譯”而不是“碰巧畫(huà)對(duì)了”。3. 邏輯結(jié)構(gòu)與物理落地范式判斷和建表 SQL 要能一起交出來(lái)E-R 圖定完下一步就是把它翻譯成關(guān)系模式然后落到建表 SQL。這一章是報(bào)告正文里技術(shù)含量最高的部分很多同學(xué)在這里只會(huì)貼一大段建表語(yǔ)句卻說(shuō)不清“為什么學(xué)生表里沒(méi)有班級(jí)名”這種問(wèn)題。我不會(huì)讓你背范式定義而是給出在工作里真正有用的判斷順序。3.1 從 1NF 到 3NF一個(gè)不繞彎的檢查順序第一范式是原子性也就是每個(gè)字段不能再拆。比如“學(xué)生”表里如果有一個(gè)字段叫“聯(lián)系方式”里面同時(shí)塞手機(jī)號(hào)和郵箱這就違反 1NF。實(shí)際設(shè)計(jì)時(shí)只要保證“一個(gè)字段只存一種信息”就不會(huì)有問(wèn)題。第二范式要求“非主屬性完全依賴(lài)主鍵”這條主要是對(duì)付聯(lián)合主鍵的典型場(chǎng)景是選課表學(xué)號(hào)課程號(hào)里如果放進(jìn)“學(xué)生姓名”姓名只依賴(lài)學(xué)號(hào)不依賴(lài)課程號(hào)就是部分依賴(lài)必須拆出去。第三范式是大多數(shù)課程設(shè)計(jì)的及格線(xiàn)它的判斷只要一句大白話(huà)非主屬性之間不能有依賴(lài)關(guān)系。最常見(jiàn)的違規(guī)例子是“學(xué)生”表里有“院系編號(hào)”又有“院系名稱(chēng)”二者都是學(xué)生表的非主屬性但院系名稱(chēng)依賴(lài)院系編號(hào)這就是傳遞依賴(lài)一旦院系改名學(xué)生表里所有相關(guān)行都要跟著改。正確做法是只留“院系編號(hào)”院系名稱(chēng)放進(jìn)院系表。在報(bào)告里明確寫(xiě)出“本設(shè)計(jì)滿(mǎn)足 3NF消除了部分依賴(lài)和傳遞依賴(lài)”并附上每個(gè)表的依賴(lài)說(shuō)明評(píng)審很難挑出硬傷。3.2 主鍵與外鍵用最簡(jiǎn)單的方案規(guī)避 80% 的翻車(chē)教務(wù)管理系統(tǒng)的主鍵選擇我強(qiáng)烈建議采用業(yè)務(wù)主鍵而不是無(wú)意義的自增主鍵。學(xué)號(hào)、課程號(hào)、工號(hào)本身在業(yè)務(wù)上就是唯一的直接拿來(lái)做主鍵既能減少一張多余的 ID 列也方便你在文檔里解釋“為什么說(shuō)這個(gè)字段可以唯一標(biāo)識(shí)實(shí)體”。自增主鍵在這個(gè)場(chǎng)景里沒(méi)有壞處但到了答辯環(huán)節(jié)“為什么不用學(xué)號(hào)當(dāng)主鍵”這個(gè)問(wèn)題會(huì)連續(xù)追問(wèn)好幾輪沒(méi)必要給自己制造這種壓力。外鍵的設(shè)置注意兩點(diǎn)。一是成績(jī)表里“學(xué)號(hào)”不僅要設(shè)外鍵還要在“學(xué)號(hào)”“課程號(hào)”上單獨(dú)建索引否則大數(shù)據(jù)量連接時(shí)會(huì)嚴(yán)重拖慢速度。二是在報(bào)告里明確外鍵更新規(guī)則刪除一個(gè)學(xué)生時(shí)其選課記錄和成績(jī)記錄應(yīng)該如何處理。常見(jiàn)答案有兩類(lèi)一是禁止刪除選過(guò)課的學(xué)生二是級(jí)聯(lián)刪除成績(jī)但保留課程記錄。數(shù)據(jù)庫(kù)課程設(shè)計(jì)一般用“選課表外鍵 ON DELETE CASCADE成績(jī)表外鍵同樣級(jí)聯(lián)”處理這樣邏輯簡(jiǎn)單且好演示。不要畫(huà)蛇添足加“觸發(fā)器”這種不需要的內(nèi)容除非你確實(shí)能講清楚它的每行代碼。3.3 建表 SQL給出一段能直接跑通的整表腳本邏輯結(jié)構(gòu)最終要落實(shí)到一段完整的建表腳本下面這段覆蓋了教務(wù)管理系統(tǒng)的核心表順序上先建被依賴(lài)的表再建含外鍵的表避免外鍵引用失敗。我用 MySQL 語(yǔ)法寫(xiě)但結(jié)構(gòu)同樣適用于其他數(shù)據(jù)庫(kù)。-- 院系表先建因?yàn)閷W(xué)生和教師都依賴(lài)它 CREATE TABLE department ( dept_id CHAR(4) PRIMARY KEY, dept_name VARCHAR(50) NOT NULL UNIQUE, dean VARCHAR(20) COMMENT 院長(zhǎng)姓名允許為空 ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 學(xué)生表依賴(lài)院系表 CREATE TABLE student ( stu_id CHAR(10) PRIMARY KEY, stu_name VARCHAR(20) NOT NULL, gender CHAR(1) NOT NULL DEFAULT 男, dept_id CHAR(4) NOT NULL, enroll_year YEAR NOT NULL, CONSTRAINT fk_stu_dept FOREIGN KEY (dept_id) REFERENCES department (dept_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 課程表不依賴(lài)其他業(yè)務(wù)表 CREATE TABLE course ( course_id CHAR(6) PRIMARY KEY, course_name VARCHAR(50) NOT NULL, credit DECIMAL(3,1) NOT NULL DEFAULT 2.0, capacity INT NOT NULL DEFAULT 60, CHECK (credit 0 AND capacity 0) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 教師表依賴(lài)院系表 CREATE TABLE teacher ( teacher_id CHAR(6) PRIMARY KEY, teacher_name VARCHAR(20) NOT NULL, dept_id CHAR(4) NOT NULL, title VARCHAR(20) COMMENT 職稱(chēng)例如副教授, CONSTRAINT fk_tea_dept FOREIGN KEY (dept_id) REFERENCES department (dept_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 選課表M:N 聯(lián)系的中間表聯(lián)合主鍵 CREATE TABLE sc ( stu_id CHAR(10) NOT NULL, course_id CHAR(6) NOT NULL, term CHAR(10) NOT NULL COMMENT 例如 2024-2025-1, score DECIMAL(5,2) COMMENT 成績(jī)未考時(shí)為空, PRIMARY KEY (stu_id, course_id, term), CONSTRAINT fk_sc_stu FOREIGN KEY (stu_id) REFERENCES student (stu_id) ON DELETE CASCADE, CONSTRAINT fk_sc_course FOREIGN KEY (course_id) REFERENCES course (course_id), KEY idx_course_id (course_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;上面這段腳本有四個(gè)參數(shù)值得在報(bào)告里單獨(dú)解釋。第一是所有表統(tǒng)一用 InnoDB理由是要支持外鍵和事務(wù)這個(gè)選擇要寫(xiě)進(jìn)物理設(shè)計(jì)說(shuō)明。第二是字符集統(tǒng)一 utf8mb4避免課程名里出現(xiàn)生僻字或特殊符號(hào)時(shí)亂碼。第三是選課表主鍵里帶了 term 學(xué)期字段這代表同一學(xué)生在同一學(xué)期不能重復(fù)選同一門(mén)課而不同學(xué)期允許再選比只有學(xué)號(hào)課程號(hào)的主鍵更符合補(bǔ)考和重修的真實(shí)業(yè)務(wù)。第四是課程表里用 CHECK 約束保證學(xué)分和容量不能為負(fù)數(shù)雖然部分?jǐn)?shù)據(jù)庫(kù)會(huì)忽略 CHECK但在設(shè)計(jì)文檔里寫(xiě)了它能證明你有完整性意識(shí)。3.4 存儲(chǔ)引擎與索引物理設(shè)計(jì)也要有一小段自己的論述報(bào)告里物理設(shè)計(jì)章節(jié)經(jīng)常被寫(xiě)成“選用 MySQL 默認(rèn)配置”這是明顯的敷衍。我建議用一小段話(huà)講清楚兩個(gè)選擇一是為什么選 InnoDB因?yàn)榻虅?wù)系統(tǒng)的成績(jī)錄入和選課操作涉及事務(wù)選課過(guò)程可能同時(shí)更新課程剩余容量和學(xué)生選課記錄InnoDB 的行級(jí)鎖和事務(wù)支持能讓這步不至于讀到臟數(shù)據(jù)二是索引策略除了主鍵成績(jī)查詢(xún)最常走的是“學(xué)號(hào) 學(xué)期”組合所以要在 sc 表上建聯(lián)合索引但不要給性別這種低區(qū)分度字段建索引性?xún)r(jià)比很低。這一段寫(xiě)出來(lái)就讓報(bào)告有了“從理論到工程決策”的落地感。4. 報(bào)告正文的寫(xiě)法把文檔目錄做成評(píng)審最容易復(fù)核的施工藍(lán)圖數(shù)據(jù)庫(kù)課程設(shè)計(jì)報(bào)告常見(jiàn)模板分七個(gè)部分需求分析、概念結(jié)構(gòu)設(shè)計(jì)、邏輯結(jié)構(gòu)設(shè)計(jì)、物理結(jié)構(gòu)設(shè)計(jì)、數(shù)據(jù)庫(kù)實(shí)現(xiàn)與測(cè)試、總結(jié)與展望、參考文獻(xiàn)。我這里不重復(fù)模板廢話(huà)只講每部分到底要放什么內(nèi)容才算合格以及哪些地方不能做成流水賬。4.1 報(bào)告目錄骨架與每章字?jǐn)?shù)分配我見(jiàn)過(guò)一批高分報(bào)告它們的共同點(diǎn)是每章都有“這一段解決了什么問(wèn)題”。你可以按下面的目錄骨架去填空頁(yè)數(shù)大概控制在 25 到 35 頁(yè)之間核心內(nèi)容放在中間四章需求分析用一段文字概括教務(wù)管理的業(yè)務(wù)流程然后列出三條核心業(yè)務(wù)規(guī)則同一學(xué)生同一學(xué)期不能重復(fù)選同一門(mén)課成績(jī)只在選課記錄存在時(shí)才能錄入刪除學(xué)生時(shí)其選課和成績(jī)數(shù)據(jù)同步處理。概念結(jié)構(gòu)設(shè)計(jì)放全局 E-R 圖附上實(shí)體和屬性的說(shuō)明文字。這一章容易犯的毛病是只貼圖不解釋后面答辯時(shí)老師會(huì)指著圖中任意一個(gè)實(shí)體問(wèn)“為什么這個(gè)屬性要放在它身上”。邏輯結(jié)構(gòu)設(shè)計(jì)描述每個(gè)關(guān)系模式按“表名屬性列表”的格式列出然后單獨(dú)挑出選課表說(shuō)明它的主鍵為什么是三列聯(lián)合主鍵。物理結(jié)構(gòu)設(shè)計(jì)建表 SQL 腳本、存儲(chǔ)引擎選擇理由、索引建立策略。數(shù)據(jù)庫(kù)實(shí)現(xiàn)與測(cè)試實(shí)際執(zhí)行的命令、插入的測(cè)試數(shù)據(jù)、關(guān)鍵的查詢(xún)驗(yàn)證結(jié)果越可復(fù)現(xiàn)越好。4.2 數(shù)據(jù)字典表格的正確填法數(shù)據(jù)字典是評(píng)審老師最常直接翻看的內(nèi)容因?yàn)樗钅荏w現(xiàn)你有沒(méi)有逐個(gè)字段思考過(guò)。不建議把整個(gè)數(shù)據(jù)庫(kù)的上百個(gè)字段全塞進(jìn)去挑五張核心表做完整數(shù)據(jù)字典即可。表格要包含字段名、類(lèi)型、長(zhǎng)度、允許空、默認(rèn)值、說(shuō)明六列。下面給一個(gè)課程表的示例格式可以直接抄字段名數(shù)據(jù)類(lèi)型長(zhǎng)度允許空默認(rèn)值說(shuō)明course_idCHAR6否無(wú)課程編號(hào)主鍵course_nameVARCHAR50否無(wú)課程全稱(chēng)creditDECIMAL3,1否2.0學(xué)分必須大于 0capacityINT11否60選課人數(shù)上限授課教師不在表中體現(xiàn)---通過(guò) sc 表關(guān)聯(lián) teacher最后一行“授課教師”這樣寫(xiě)是故意的它告訴評(píng)審你理解教師與課程的關(guān)聯(lián)是通過(guò)中間表實(shí)現(xiàn)的而不是在課程表里放一個(gè)冗余字段。數(shù)據(jù)字典里能出現(xiàn)這種“說(shuō)明設(shè)計(jì)決策”的注釋比堆字段強(qiáng)得多。4.3 測(cè)試與實(shí)現(xiàn)部分曬運(yùn)行痕跡而不是曬功能清單報(bào)告里的“測(cè)試”是最容易被寫(xiě)成假大空的地方。很多同學(xué)會(huì)寫(xiě)“功能測(cè)試通過(guò)、界面顯示正?!钡n程設(shè)計(jì)不是軟件工程課數(shù)據(jù)庫(kù)課程設(shè)計(jì)的測(cè)試重心在于你能不能用 SQL 證明設(shè)計(jì)是對(duì)的。我建議在測(cè)試章節(jié)放這幾樣?xùn)|西至少十條有代表性的 INSERT 數(shù)據(jù)其中必須包含邊緣數(shù)據(jù)比如入學(xué)年份為 2000 年的學(xué)生、容量為 1 的課程至少五條 SELECT 查詢(xún)覆蓋單表查詢(xún)、多表連接、分組統(tǒng)計(jì)、帶 HAVING 的聚合查詢(xún)、子查詢(xún)各一條一條 UPDATE 加一條 DELETE用來(lái)展示級(jí)聯(lián)刪除效果。每一段 SQL 后面跟一行“執(zhí)行結(jié)果截圖”這個(gè)動(dòng)作本身就證明你的數(shù)據(jù)庫(kù)不是紙上談兵。5. 教務(wù)管理系統(tǒng)數(shù)據(jù)庫(kù)設(shè)計(jì)避坑評(píng)審最容易挑出的幾個(gè)翻車(chē)點(diǎn)前面都在講“怎么做對(duì)”這一章集中講我見(jiàn)過(guò)最多的“怎么翻車(chē)”每條都是按現(xiàn)象、原因、解決三個(gè)層次拆解的直接對(duì)著避雷。5.1 現(xiàn)象E-R 圖上的 M:N 聯(lián)系在關(guān)系模式里消失有同學(xué)畫(huà)圖時(shí)學(xué)生和課程之間畫(huà)了明顯的菱形“選課”但到了關(guān)系模式設(shè)計(jì)章節(jié)只在“學(xué)生”表里加了一個(gè)字段叫“已選課程”用逗號(hào)分隔課程號(hào)。理由是這樣查詢(xún)方便。評(píng)審當(dāng)場(chǎng)問(wèn)了一句“請(qǐng)寫(xiě)一條 SQL查出選了課程號(hào)為 C001 的所有學(xué)生名單”他寫(xiě)不出來(lái)因?yàn)樽址ヅ錈o(wú)法走索引效率極差。原因是把關(guān)系模型的規(guī)范化原則理解成了“能省則省”實(shí)際恰恰相反M:N 聯(lián)系必須拆表。解決方式就是前面寫(xiě)的選課表 sc把多對(duì)多關(guān)系翻譯成獨(dú)立實(shí)體關(guān)系這是最標(biāo)準(zhǔn)也最穩(wěn)的答案。5.2 現(xiàn)象主鍵用了自增 ID業(yè)務(wù)唯一字段反而沒(méi)有唯一約束某個(gè)成績(jī)表里設(shè)計(jì)成無(wú)意義的自增主鍵學(xué)生字段和課程字段只是普通外鍵結(jié)果測(cè)試時(shí)發(fā)現(xiàn)同一學(xué)生同一門(mén)課錄入了兩條成績(jī)系統(tǒng)沒(méi)有攔截。原因是把“記錄編號(hào)”和“業(yè)務(wù)唯一性”混為一談自增主鍵只能保證每行不同不能保證業(yè)務(wù)上不重復(fù)。解決方式是采用聯(lián)合業(yè)務(wù)主鍵設(shè)計(jì)時(shí)先問(wèn)“什么樣的組合在業(yè)務(wù)里只能出現(xiàn)一次”對(duì)這個(gè)組合加主鍵約束或唯一約束再把自增列作為普通索引字段。5.3 現(xiàn)象建庫(kù)時(shí)沒(méi)指定字符集插入中文變成亂碼這屬于環(huán)境問(wèn)題但答辯時(shí)一旦演示翻車(chē)非??鄯?。原因通常是建庫(kù)語(yǔ)句寫(xiě)了 CREATE DATABASE school; 省略了字符集參數(shù)而客戶(hù)端連接字符集與實(shí)際存儲(chǔ)字符集不一致中文寫(xiě)入后亂碼。解決方式是在所有建庫(kù)建表語(yǔ)句里顯式加 DEFAULT CHARSETutf8mb4同時(shí)連接字符串里設(shè)置 characterEncodingutf8并在測(cè)試數(shù)據(jù)腳本里從第一行就用中文盡早暴露出問(wèn)題。這個(gè)坑不涉及高深理論但每年都有人中招。5.4 現(xiàn)象選課表的外鍵沒(méi)加級(jí)聯(lián)規(guī)則刪除學(xué)生時(shí)報(bào)錯(cuò)下不去寫(xiě) DELETE FROM student WHERE stu_id... 時(shí)因?yàn)檫x課表仍引用該學(xué)號(hào)被外鍵約束擋住報(bào)錯(cuò)信息一看就像數(shù)據(jù)庫(kù)故障現(xiàn)場(chǎng)答疑時(shí)如果講不清場(chǎng)面會(huì)很尷尬。原因是對(duì)外鍵約束的默認(rèn)行為沒(méi)有預(yù)期MySQL 默認(rèn) RESTRICT也就是有引用關(guān)系的行不允許刪除。解決方式是在建 sc 表時(shí)給外鍵顯式聲明 ON DELETE CASCADE并在報(bào)告里用一小段數(shù)據(jù)演示“刪除一個(gè)學(xué)生后他的選課記錄同步消失”來(lái)證明級(jí)聯(lián)生效。這一步是現(xiàn)場(chǎng)演示的加分亮點(diǎn)不要跳過(guò)。5.5 現(xiàn)象報(bào)告缺少可執(zhí)行 SQL 腳本答辯老師要求現(xiàn)場(chǎng)重跑建表有的報(bào)告把建表 SQL 拆散在正文里字段解釋用文字描述結(jié)果現(xiàn)場(chǎng)老師說(shuō)“把你整個(gè)建庫(kù)到驗(yàn)證的過(guò)程完整跑一遍”同學(xué)只能從文檔里一個(gè)個(gè)復(fù)制語(yǔ)句先跑哪張后跑哪張完全沒(méi)順序中途報(bào)外鍵錯(cuò)誤。原因是報(bào)告沒(méi)有提供一份可重復(fù)執(zhí)行的完整腳本也說(shuō)明作者自己試驗(yàn)時(shí)就是零散操作的。解決方式是把第三章的建表語(yǔ)句、第五章的測(cè)試數(shù)據(jù)、查詢(xún)驗(yàn)證語(yǔ)句合并成一個(gè) sql 文件文件名按順序編號(hào)并在文檔的“數(shù)據(jù)庫(kù)實(shí)現(xiàn)”章節(jié)標(biāo)明執(zhí)行順序。這是低成本卻能極大提升報(bào)告完成度的動(dòng)作。6. 收在可復(fù)現(xiàn)的一步給自己留一段“先刪后建”的整體驗(yàn)證腳本最后一章不講新理論給你一個(gè)我每次做這類(lèi)設(shè)計(jì)都會(huì)留到最后執(zhí)行的動(dòng)作把整個(gè)數(shù)據(jù)庫(kù)變成一份“無(wú)論何時(shí)運(yùn)行都能得到同樣結(jié)果”的腳本。這個(gè)習(xí)慣保證你答辯前十分鐘不會(huì)因?yàn)閿?shù)據(jù)庫(kù)被改亂了而翻車(chē)。下面是腳本的骨架正好用到一個(gè)常用的清理技巧。-- 先刪庫(kù)再建庫(kù)保證每次執(zhí)行都在干凈環(huán)境里 DROP DATABASE IF EXISTS school; CREATE DATABASE school DEFAULT CHARSETutf8mb4; USE school; -- 從這里開(kāi)始按順序粘貼第 3.3 節(jié)的所有建表語(yǔ)句 SOURCE /home/username/db_design/schema.sql; -- 插入測(cè)試數(shù)據(jù)至少包含邊緣數(shù)據(jù) INSERT INTO student (stu_id, stu_name, gender, dept_id, enroll_year) VALUES (20240001, 張三, 男, D001, 2024), (20240002, 李四, 女, D002, 2024); -- 驗(yàn)證選課人數(shù)與課程容量對(duì)比 SELECT sc.course_id, COUNT(sc.stu_id) AS selected_total, c.capacity FROM sc JOIN course c ON sc.course_id c.course_id GROUP BY sc.course_id, c.capacity HAVING COUNT(sc.stu_id) c.capacity;腳本里最關(guān)鍵的不是建表語(yǔ)句而是最上面的 DROP DATABASE IF EXISTS。它聽(tīng)起來(lái)簡(jiǎn)單但能保障測(cè)試環(huán)境的確定性你反復(fù)運(yùn)行多少次都不會(huì)因?yàn)闅埩魯?shù)據(jù)導(dǎo)致結(jié)果不一致。我習(xí)慣在寫(xiě)完所有 SQL 后把整個(gè)文件從頭跑一遍然后關(guān)閉數(shù)據(jù)庫(kù)連接再打開(kāi)重跑一次確認(rèn)無(wú)狀態(tài)依賴(lài)。上面最后那條查詢(xún)是一個(gè)自檢思路如果選課人數(shù)超過(guò)容量說(shuō)明測(cè)試數(shù)據(jù)設(shè)計(jì)不合理或者應(yīng)用層漏掉了容量校驗(yàn)此時(shí)去改測(cè)試數(shù)據(jù)而不是改查詢(xún)。這種“拿查詢(xún)驗(yàn)證業(yè)務(wù)規(guī)則”的意識(shí)恰好是課程設(shè)計(jì)里最容易被忽略又最加分的部分。答辯前我會(huì)額外做兩個(gè)檢查動(dòng)作。第一把 SOURCE 后面的路徑改成絕對(duì)路徑避免相對(duì)路徑在不同電腦上找不到文件第二在腳本末尾加一條 SHOW TABLES; 檢查五張表是否齊全再執(zhí)行 SELECT COUNT(*) FROM sc; 對(duì)著測(cè)試數(shù)據(jù)核對(duì)行數(shù)。這兩個(gè)動(dòng)作加起來(lái)只需要兩分鐘卻能把九成環(huán)境類(lèi)故障擋在門(mén)外。數(shù)據(jù)庫(kù)課程設(shè)計(jì)這門(mén)課本質(zhì)上是讓你體會(huì)“一個(gè)看似簡(jiǎn)單的選課系統(tǒng)落到表結(jié)構(gòu)上需要做多少謹(jǐn)慎決策”。你可以不用華麗的功能界面但建表腳本和報(bào)告里的每一段推導(dǎo)都該經(jīng)得起追問(wèn)。希望這些經(jīng)驗(yàn)?zāi)軒湍闵僮咭欢螐澛纷龀鲆环菽茏屪约悍判慕怀鋈サ恼n程設(shè)計(jì)。本文還有配套的精品資源點(diǎn)擊獲取