首页 / 资讯中心 / 文章详情

医院门诊管理系统数据库设计:从需求分析到建表落地

医院门诊管理系统数据库设计:从需求分析到建表落地 ★ FEATURED ARTICLE
简介这是一份医院门诊管理系统数据库设计的课程设计文档适合软件工程、数据库相关专业学生及需要完成类似课设的开发者参考。资源围绕小型医院门诊管理系统的数据库设计与实现展开涵盖需求分析、数据流程图、数据字典、E-R图设计、概念与逻辑结构设计、物理设计及SQL Server 2008环境下的数据库实施与测试可帮助读者理解从业务梳理到表结构落地的完整设计流程。压缩包共1个文件为doc格式文档大小731KB正文包含目录、摘要、详细章节及图表说明结构完整。该资源已有2832人学习适合作为课程设计开题、论文撰写或系统原型设计的参考资料尤其便于对照学习结构化分析方法与数据库规范化处理的具体应用。1. 医院门诊管理系统课程设计为什么大多数提交都卡在同一个地方医院门诊管理系统数据库设计几乎是每个高校计算机专业课程设计清单里的常驻题目。说它简单是因为业务场景足够熟悉挂号、就诊、开药、缴费人人都去过医院说它难是因为一旦动手你会发现真正决定这份课程设计质量的根本不是你用了多少张表而是关系模式设计得是否规范、数据约束是否兜得住业务、文档里能不能把每一步设计决策讲清楚。A同学把 E-R 图画得精致漂亮却因为患者挂号和医生排班这两块没有理清关联被导师一句话问住了。这门课程设计要交付的核心物是一份数据库设计文档通常包含需求分析、E-R 模型、关系模式、建库建表 SQL、数据字典和测试说明。它考察的并不是你能不能写几条 CREATE TABLE而是你有没有能力把“门诊业务”翻译成结构化、低冗余、可扩展的数据模型。新手和熟手的分水岭恰恰在“业务规则到表约束的转化”这一段——比如一个号源被挂出后如何防止超挂一个患者的多次就诊记录如何稳定关联。这篇文章按我在类似项目里沉淀下来的可靠做法把从需求分析到最终文档成稿的完整路径拆开讲透每步的建模决策依据、可照抄的建表脚本、参数该怎么定、以及那些你大概率会遇到的坑。目标是让你照着做能独立产出一份有逻辑深度、能通过答辩的完整设计。2. 需求先行画对 E-R 图之前先把五条业务规则定死2.1 门诊核心流程的实体识别从挂号和诊疗中抽实体很多同学上来就画 E-R 图结果画到一半发现实体越来越多、关系乱成一团。根源在于没有先做需求分析直接从直觉跳到结构设计。我习惯的做法是先列业务流程再从中抽实体。门诊业务流程最简版本是这样的患者到医院 → 挂号选择科室和医生→ 医生接诊 → 开具检查或处方 → 患者缴费 → 可能取药。把这个流程走一遍实体其实已经浮现了患者、科室、医生、排班号源、挂号单、处方、处方明细、药品、收费记录。其中有两个实体特别容易被忽略一个是“排班”另一个是“处方明细”。没有排班表你无法回答“某医生某天上午放了多少号、还剩几个号”这类问题没有处方明细表一张处方开了多种药就没有地方存。实体识别到这里不要急着画图先问自己五个问题一个患者可以挂多次号吗一次挂号对应一次就诊记录吗一条就诊记录能对应多张处方吗一张处方最多能包含几种药一个医生隶属一个科室还是多个科室这些问题在需求阶段不确定后面表结构一定返工。2.2 三个关键业务规则挂号的号源控制、处方与药品的关联、就诊历史的时间追溯在实体基本清楚后我建议把业务规则明确成文档里的“约束清单”这是课程设计答辩时最容易被提问的部分。根据常见方案门诊系统的核心规则有三条。第一条是号源控制一个排班记录代表某医生在一个时间段比如上午的一批号源每个号源在某个时刻要么是“未挂出”要么被某个挂号单占用。数据库层面如何保证不超挂两种常见做法是在挂号单表里对排班ID做唯一约束或者更实际一点——在生成号源时逐条插入号源表通过“号源状态”字段配合事务控制。综合来看第二种做法更贴近真实系统也更容易通过老师的“并发提问”。第二条是处方状态流转处方有“已开立、已缴费、已作废”等状态缴费动作应当被记录到缴费表中而不是简单地修改处方表里的一个字段。因为课程设计要体现数据一致性缴费记录表和处方状态必须能被对账。第三条是就诊历史的完整保留患者的每次就诊、每个诊断、每张处方都要能按时间维度完整查询。这意味着所有表都应该有创建时间字段CREATE_TIME并且删除操作尽量用逻辑删除字段IS_DELETED代替物理删除方便论文中写“支持历史数据追溯”。2.3 从 E-R 图到关系模式的映射规则一对多与多对多的标准处理E-R 图转关系模式课程教材里有一套标准流程实体转关系、1对1和1对N联系归并到N端的表、M对N联系独立建表。到了门诊系统的场景里具体映射是这样的。科室与医生1 对 N医生表中加 DEPT_ID 外键即可。医生与排班1 对 N排班表中加 DOCTOR_ID 外键。排班与号源1 对 N号源表中加 SCHEDULE_ID 外键。患者与挂号单1 对 N挂号单表中加 PATIENT_ID 外键。挂号单与号源1 对 1这一步最容易处理错——挂号单应该引用号源表的主键并将号源表主键作为外键约束同时给这个字段加唯一约束。处方与药品M 对 N必须拆出处方明细表明细表同时持有处方ID和药品ID两个外键。这套映射做完后表数量就确定了。按我在模拟项目X中的实践核心表十张左右科室表、医生表、排班表、号源表如果与排班合并则不需要、患者表、挂号单表、就诊记录表、处方表、处方明细表、药品表、收费记录表。注意如果排班和号源合并成一张表——即每条排班生成一个具体号源行——那么表会少一张但“某个时间段内号源总量”的语义要额外加字段来支持。我倾向拆开逻辑更清晰答辩更好讲。3. 写清逻辑设计关系模式、函数依赖与三大范式的落地取舍3.1 关系模式清单与主外键定义关系模式是课程设计文档里权重最高的部分导师会逐条核对表名、字段名、类型、主外键。以下是一套可直接使用的核心关系模式基于常见设计方案整理关键字段后标注了设计理由。科室表 DEPTDEPT_ID, DEPT_NAME, DEPT_LOCATION, CREATE_TIME。主键 DEPT_ID。科室名称要加唯一约束避免同一科室被录入两次。医生表 DOCTORDOCTOR_ID, DOCTOR_NAME, DEPT_ID, TITLE, IS_DELETED, CREATE_TIME。主键 DOCTOR_ID外键 DEPT_ID 参照科室表。TITLE 字段存放职称主任医师、副主任医师等用 VARCHAR 存中文还是用 CHAR 存编码均可课程设计建议直接存中文展示时少做一次关联。排班表 SCHEDULESCHEDULE_ID, DOCTOR_ID, SCHEDULE_DATE, PERIOD_TYPE, TOTAL_NUM, REGISTERED_NUM, CREATE_TIME。主键 SCHEDULE_ID外键 DOCTOR_ID。PERIOD_TYPE 用 TINYINT 存1 上午 / 2 下午TOTAL_NUM 和 REGISTERED_NUM 的差值就是剩余号源数量。字段上可以加一个 CHECK 约束确保 REGISTERED_NUM 不大于 TOTAL_NUM。号源表 SOURCESOURCE_ID, SCHEDULE_ID, DOCTOR_ID, SOURCE_TIME, STATUS, CREATE_TIME。主键 SOURCE_ID外键 SCHEDULE_ID 和 DOCTOR_ID。STATUS 用 TINYINT0 未挂号、1 已挂号、2 已作废。SOURCE_TIME 是具体到分钟的就诊时间点。患者表 PATIENTPATIENT_ID, PATIENT_NAME, GENDER, BIRTH_DATE, ID_CARD_NO, PHONE, CREATE_TIME。主键 PATIENT_ID。ID_CARD_NO 或者 PHONE 建议加唯一约束因为同一患者重复建档在现实中很常见。挂号单表 REGISTRATIONREG_ID, PATIENT_ID, DEPT_ID, DOCTOR_ID, SOURCE_ID, REG_TIME, STATUS, CREATE_TIME。主键 REG_ID外键 PATIENT_ID、DEPT_ID、DOCTOR_ID、SOURCE_ID。REG_TIME 记录挂号完成时刻STATUS 记录挂号状态0 正常 / 1 已退号。就诊记录表 VISITVISIT_ID, REG_ID, PATIENT_ID, DOCTOR_ID, DIAGNOSIS, VISIT_TIME, CREATE_TIME。主键 VISIT_ID外键 REG_ID 加唯一约束一次挂号对应一次就诊PATIENT_ID、DOCTOR_ID 做外键。DIAGNOSIS 存医生的诊断文本。处方表 PRESCRIPTIONPRES_ID, VISIT_ID, PATIENT_ID, DOCTOR_ID, PRES_TIME, TOTAL_AMOUNT, STATUS, CREATE_TIME。主键 PRES_ID外键 VISIT_ID。STATUS 存处方状态0 已开立 / 1 已缴费 / 2 已作废。TOTAL_AMOUNT 由明细表汇总得到。处方明细表 PREScITEMITEM_ID, PRES_ID, DRUG_ID, DRUG_COUNT, DRUG_PRICE, AMOUNT。主键 ITEM_ID外键 PRES_ID 和 DRUG_ID。为什么要冗余 DRUG_PRICE因为药品价格会调整处方上的价格必须保留开单时的价格快照否则对账会有问题。药品表 DRUGDRUG_ID, DRUG_NAME, SPECIFICATION, UNIT_PRICE, STOCK_NUM, CREATE_TIME。主键 DRUG_ID。3.2 函数依赖分析为什么处方明细表必须单独存在在文档里单开一节写函数依赖分析是拉高评分最实惠的方法。你不需要写复杂的推导把核心依赖列出来即可。在处方表 PRESCRIPTION 中PRES_ID 决定 VISIT_ID、PATIENT_ID、DOCTOR_ID、PRES_TIME、TOTAL_AMOUNT、STATUS这些是非主属性对主键的完全函数依赖。在明细表 PREScITEM 中主键是复合的PRES_ID, DRUG_ID而 DRUG_COUNT、DRUG_PRICE、AMOUNT 依赖整个复合主键而不是只依赖其中一部分——这正是必须拆出明细表的理论依据。如果把药品信息直接塞进处方表就会出现部分函数依赖导致插入异常和删除异常。相关完整的依赖描述写到这一步你就有底气回答“为什么药品不能直接冗余在处方表里”这个问题了因为处方和药品是多对多关系共存的非主属性数量、金额依赖两者的组合不满足第二范式必须拆表。3.3 三大范式到底怎么取舍别为了范式牺牲可查询性第三范式要求非主属性不传递依赖于主键。在门诊系统里严格满足第三范式会让某些查询变得啰嗦。比如医生表的 TITLE 字段它直接依赖 DOCTOR_ID没问题。但如果你在挂号单表里冗余一个医生姓名那就违反了第三范式——医生改名会导致挂号单历史记录跟着矛盾。那课程设计是不是必须三级范式全满足我的经验是核心业务表必须满足但允许有意识地做少量冗余并在文档里写明理由。两个实例挂号单表冗余 DEPT_NAME 而非只存 DEPT_ID是为了查询历史挂号记录按科室筛选时少关联一次。这个冗余控制在一个字段且来源稳定科室名称几乎不修改可以接受。处方明细表冗余 DRUG_PRICE 快照不是为了范式而是为了业务正确性——药品调价后已开出的处方不能受影响。这属于“业务要求的受控冗余”在文档里说明后导师不但不会扣分反而会认可你的思考深度。4. 物理建表一套能直接跑通的门诊系统建库脚本4.1 建库与基础表的完整 SQL 脚本下面这套脚本基于 MySQL 8.0 编写MySQL 是课程设计最主流的选型。如果你的环境是 SQL Server 或 Oracle类型上把 DATETIME 换成对应的时间类型即可。-- 创建数据库指定字符集避免中文乱码 CREATE DATABASE IF NOT EXISTS outpatient_db DEFAULT CHARACTER SET utf8mb4 DEFAULT COLLATE utf8mb4_general_ci; USE outpatient_db; -- 科室表 CREATE TABLE dept ( dept_id INT AUTO_INCREMENT COMMENT 科室ID自增主键, dept_name VARCHAR(50) NOT NULL COMMENT 科室名称, dept_location VARCHAR(100) COMMENT 科室位置, create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 创建时间, PRIMARY KEY (dept_id), UNIQUE KEY uk_dept_name (dept_name) ) ENGINEInnoDB COMMENT科室表; -- 医生表 CREATE TABLE doctor ( doctor_id INT AUTO_INCREMENT COMMENT 医生ID, doctor_name VARCHAR(50) NOT NULL COMMENT 医生姓名, dept_id INT NOT NULL COMMENT 所属科室ID, title VARCHAR(30) COMMENT 职称, is_deleted TINYINT DEFAULT 0 COMMENT 逻辑删除0正常 1删除, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (doctor_id), KEY idx_dept_id (dept_id), CONSTRAINT fk_doctor_dept FOREIGN KEY (dept_id) REFERENCES dept (dept_id) ) ENGINEInnoDB COMMENT医生表;逻辑说明dept_id 用自增整数做主键比用 UUID 好——排序快、索引占用小、展示直观。doctor 表外键 dept_id 必须有索引MySQL 在外键约束时不会自动建索引手动加 KEY 是常规操作。IS_DELETED 字段属于逻辑删除设计在文档中说明用于保留历史就诊数据。4.2 排班与号源防止超挂的核心表-- 排班表某医生某天某个时段放出一批号 CREATE TABLE schedule ( schedule_id INT AUTO_INCREMENT COMMENT 排班ID, doctor_id INT NOT NULL COMMENT 医生ID, schedule_date DATE NOT NULL COMMENT 出诊日期, period_type TINYINT NOT NULL COMMENT 时段1上午 2下午, total_num INT NOT NULL COMMENT 号源总量, registered_num INT DEFAULT 0 COMMENT 已挂号数, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (schedule_id), UNIQUE KEY uk_doctor_date_period (doctor_id, schedule_date, period_type), CONSTRAINT fk_schedule_doctor FOREIGN KEY (doctor_id) REFERENCES doctor (doctor_id), CONSTRAINT chk_registered_not_exceed_total CHECK (registered_num 0 AND registered_num total_num) ) ENGINEInnoDB COMMENT排班表; -- 号源表排班下每个具体时间点对应一个号 CREATE TABLE source ( source_id INT AUTO_INCREMENT COMMENT 号源ID, schedule_id INT NOT NULL COMMENT 排班ID, doctor_id INT NOT NULL COMMENT 医生ID, source_time DATETIME NOT NULL COMMENT 具体就诊时间点, status TINYINT DEFAULT 0 COMMENT 0未挂 1已挂 2作废, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (source_id), KEY idx_schedule_id (schedule_id), CONSTRAINT fk_source_schedule FOREIGN KEY (schedule_id) REFERENCES schedule (schedule_id), CONSTRAINT fk_source_doctor FOREIGN KEY (doctor_id) REFERENCES doctor (doctor_id) ) ENGINEInnoDB COMMENT号源表;参数说明与逻辑说明UNIQUE(doctor_id, schedule_date, period_type) 限定了同一位医生同一天同一时段只能有一个排班记录这是防止重复放号的第一道闸门。CHECK 约束保证已挂号数不能超过总数这是第二道闸门。到号源表这里每个 source 具体到分钟比如 2025-06-10 08:30:00状态由 0 变 1 时必须让挂号事务同时更新 schedule.registered_num。这两张表加一个事务就能把“超挂”问题从机制上解决。注意点MySQL 8.0.16 之前CHECK 约束会被解析但不会强制执行如果你用的版本低于 8.0.16这个约束实际是无效的。课程设计如果装的是 5.7建议改用触发器或者在代码层控制。在文档里写清楚这个版本差异反而能体现你踩过坑。4.3 患者、挂号与处方时间戳和状态字段的设计-- 患者表 CREATE TABLE patient ( patient_id INT AUTO_INCREMENT COMMENT 患者ID, patient_name VARCHAR(50) NOT NULL, gender TINYINT COMMENT 0未知 1男 2女, birth_date DATE COMMENT 出生日期, id_card_no VARCHAR(18) COMMENT 身份证号, phone VARCHAR(20) COMMENT 联系电话, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (patient_id), UNIQUE KEY uk_id_card (id_card_no), KEY idx_phone (phone) ) ENGINEInnoDB COMMENT患者表; -- 挂号单表 CREATE TABLE registration ( reg_id INT AUTO_INCREMENT COMMENT 挂号单ID, patient_id INT NOT NULL, dept_id INT NOT NULL, doctor_id INT NOT NULL, source_id INT NOT NULL COMMENT 号源ID, reg_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 挂号时间, status TINYINT DEFAULT 0 COMMENT 0正常 1已退号, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (reg_id), UNIQUE KEY uk_source_id (source_id), KEY idx_patient_id (patient_id), KEY idx_doctor_id (doctor_id), CONSTRAINT fk_reg_patient FOREIGN KEY (patient_id) REFERENCES patient (patient_id), CONSTRAINT fk_reg_dept FOREIGN KEY (dept_id) REFERENCES dept (dept_id), CONSTRAINT fk_reg_doctor FOREIGN KEY (doctor_id) REFERENCES doctor (doctor_id), CONSTRAINT fk_reg_source FOREIGN KEY (source_id) REFERENCES source (source_id) ) ENGINEInnoDB COMMENT挂号单表; -- 就诊记录表 CREATE TABLE visit ( visit_id INT AUTO_INCREMENT COMMENT 就诊ID, reg_id INT NOT NULL COMMENT 挂号单ID, patient_id INT NOT NULL, doctor_id INT NOT NULL, diagnosis VARCHAR(500) COMMENT 医生诊断, visit_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 就诊时间, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (visit_id), UNIQUE KEY uk_reg_id (reg_id), CONSTRAINT fk_visit_reg FOREIGN KEY (reg_id) REFERENCES registration (reg_id), CONSTRAINT fk_visit_patient FOREIGN KEY (patient_id) REFERENCES patient (patient_id), CONSTRAINT fk_visit_doctor FOREIGN KEY (doctor_id) REFERENCES doctor (doctor_id) ) ENGINEInnoDB COMMENT就诊记录表;逻辑说明registration 表的 uk_source_id 是“一个号源只能被挂一次”的数据库强约束加上它之后即使代码层面忘记判断号源状态数据库也会拒绝重复挂号。visit 表通过 uk_reg_id 与挂号单保持一对一这符合“挂了号才会就诊”的现实流程。patient_id 和 doctor_id 在 visit 里再存一遍是为了查询“某个患者的所有历史就诊记录”和“某个医生的所有接诊记录”时不需要回表连接属于适当的查询冗余。4.4 处方与药品金额快照与级联策略的确定-- 药品表 CREATE TABLE drug ( drug_id INT AUTO_INCREMENT COMMENT 药品ID, drug_name VARCHAR(100) NOT NULL COMMENT 药品通用名, specification VARCHAR(50) COMMENT 规格如 0.25g*24粒, unit_price DECIMAL(10,2) NOT NULL COMMENT 单价, stock_num INT DEFAULT 0 COMMENT 库存, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (drug_id) ) ENGINEInnoDB COMMENT药品表; -- 处方表 CREATE TABLE prescription ( pres_id INT AUTO_INCREMENT COMMENT 处方ID, visit_id INT NOT NULL COMMENT 就诊记录ID, patient_id INT NOT NULL, doctor_id INT NOT NULL, pres_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT 开方时间, total_amount DECIMAL(10,2) DEFAULT 0.00 COMMENT 总金额, status TINYINT DEFAULT 0 COMMENT 0已开立 1已缴费 2已作废, create_time DATETIME DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (pres_id), KEY idx_visit_id (visit_id), CONSTRAINT fk_pres_visit FOREIGN KEY (visit_id) REFERENCES visit (visit_id), CONSTRAINT fk_pres_patient FOREIGN KEY (patient_id) REFERENCES patient (patient_id), CONSTRAINT fk_pres_doctor FOREIGN KEY (doctor_id) REFERENCES doctor (doctor_id) ) ENGINEInnoDB COMMENT处方表; -- 处方明细表 CREATE TABLE pres_item ( item_id INT AUTO_INCREMENT COMMENT 明细ID, pres_id INT NOT NULL COMMENT 处方ID, drug_id INT NOT NULL COMMENT 药品ID, drug_count INT NOT NULL COMMENT 数量, drug_price DECIMAL(10,2) NOT NULL COMMENT 开单时药品单价快照, amount DECIMAL(10,2) NOT NULL COMMENT 单项金额 数量 * 单价快照, PRIMARY KEY (item_id), KEY idx_pres_id (pres_id), KEY idx_drug_id (drug_id), CONSTRAINT fk_item_pres FOREIGN KEY (pres_id) REFERENCES prescription (pres_id), CONSTRAINT fk_item_drug FOREIGN KEY (drug_id) REFERENCES drug (drug_id), CONSTRAINT chk_amount_positive CHECK (amount 0) ) ENGINEInnoDB COMMENT处方明细表;参数说明与取舍逻辑DECIMAL(10,2) 是金额的标准选择10 位总精度、2 位小数满足单张处方金额到千万元级别且不会出现 FLOAT 的精度漂移。drug_price 在明细表里快照单价可以在药品表里随便改历史处方不受影响。外键约束统一不加 ON DELETE CASCADE原因在避坑章节详说。如果还要加收费表结构与处方表类似记录缴费时间、缴费金额、支付方式与处方一对一或一对多均可看你要不要支持一张处方分多次缴费我的建议是保持一对一流程更清晰。5. 避坑门诊系统设计里最常见的六个翻车现场5.1 时间字段用 VARCHAR 存储现象挂号和排班时间用 VARCHAR(20) 存格式像“2025-06-10 08:30”。原因写文档时图省事前端传什么就存什么。后果是查询某天的号源时必须 LIKE 2025-06-10% 才能匹配无法用 BETWEEN 高效索引。处理方式MySQL 里时间一律用 DATE、DATETIME、TIMESTAMP。TIMESTAMP 有 2038 年的上限课程设计无所谓但记住这个边界对工作后用得上。教训建表时偷懒选字符串类型后面写 SQL 时每一行都会还债。5.2 外键不建或者乱建导致数据互相冲突现象医生表 dept_id 建了外键但删科室时直接 DELETE被 MySQL 拒绝提示外键约束失败。或者反过来所有表都不建外键靠代码逻辑维护结果出现孤儿数据挂号单指向一个已经删除的医生。原因是对“物理删除和逻辑删除”的边界没想清楚。处理方式对核心业务表一律用逻辑删除is_deleted 字段避免物理删除触发外键冲突外键要建但全部采用默认的 RESTRICT 策略不允许级联删除。这样删除科室时先停用医生、处理历史单流程是可控的。5.3 排班与号源概念混淆一张表搞定导致放号无记录现象只用一张 schedule 表字段里有 total_num 和 registered_num但没有具体到每个时间点的号源记录。于是无法回答“8 点 30 的号挂了没有”。原因没区分“放号批次”和“单个号源”两个数据粒度。处理方式按本章的 schedule source 两表设计。在文档中解释两张表的分工schedule 描述批次source 描述个体。如果觉得两张表麻烦至少要在 schedule 表里增加 remark 字段记取值范围但这只是临时方案答辩容易被追问。5.4 金额字段用 FLOAT 或 DOUBLE现象处方金额对账时FLOAT 累加出现 0.0000001 的偏差医生端和收费端显示不一致。原因 FLOAT 是二进制浮点数无法精确表示十进制小数这是计算机组成原理的经典知识却在课程设计里反复翻车。处理方式金额、单价一律 DECIMAL(10,2)。在文档的“数据类型选择说明”里写一句“DECIMAL 是定长定点数不涉及浮点误差”这句话在本章节这个场景下就是评分细节。5.5 唯一约束缺失同一病人重复建档现象患者表没有身份证号唯一约束同一患者被建档两次历史就诊记录被拆到两个 patient_id 下查询时漏数据。原因当时觉得加不加唯一约束无所谓反正系统是自己写的。处理方式id_card_no 加 UNIQUE如果存在无身份证的婴幼儿或外籍人员id_card_no 允许 NULL但 MySQL 允许 NULL 重复所以再加一个逻辑如果身份证号为空用 patient_name birth_date guardian_phone 做业务查重即可课程设计里写出这个策略能体现考虑周全。5.6 忘记设计数据字典文档只有建表 SQL现象整套文档翻下来只有 CREATE TABLE 语句没有字段说明表字段名、类型、含义、取值、主外键答辩时老师问一个 STATUS 字段的取值都答得磕巴。原因把“数据库设计”误解为“写建表语句”。处理方式每张表后附一个字段说明表格列名 / 数据类型 / 允许空 / 键属性 / 含义描述 / 取值说明。这个章节可以直接做成附录工作量不大但对文档完整度提升非常明显。数据字典也是课程设计评分标准里单独列出的考察项不加等于白丢分。6. 从建表到成稿视图、存储过程与课程设计文档的组装顺序6.1 几个能直观展示设计亮点的视图视图是课程设计里的加分项也是很多同学完全没写的部分。写两个有业务意义、又能展示查询能力的视图即可。第一个当日各科室号源剩余量视图让老师直观看到“剩余号”从哪来。CREATE VIEW v_dept_source_remain AS SELECT d.dept_id, d.dept_name, s.schedule_date, SUM(s.total_num) AS total, SUM(s.total_num - s.registered_num) AS remain FROM dept d JOIN doctor doc ON d.dept_id doc.dept_id JOIN schedule s ON doc.doctor_id s.doctor_id WHERE s.schedule_date CURDATE() GROUP BY d.dept_id, d.dept_name, s.schedule_date;逻辑说明这个视图把科室、医生、排班三张表关联按科室聚合出当日总数和剩余数。CURDATE() 取当天如果要做测试可以把 schedule_date 改成具体日期。第二个视图是患者历史就诊概览关联 patient、visit、prescription 三张表展示某患者历次就诊时间和诊断文本用于支持“按患者查历史”功能。视图的好处是让老师看到你有“封装复杂查询”的意识比在文档里贴一堆长 SQL 更直观。6.2 一个支撑挂号和退号流程的存储过程存储过程建议写一个挂号事务把“占用号源 更新排班已挂号数 插入挂号单”包在一个事务里。这既是业务核心又是并发控制的最佳展示载体。DELIMITER $$ CREATE PROCEDURE sp_register( IN p_patient_id INT, IN p_source_id INT, OUT p_reg_id INT, OUT p_msg VARCHAR(50) ) BEGIN DECLARE v_status TINYINT DEFAULT -1; DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; SET p_msg 挂号失败事务已回滚; END; START TRANSACTION; SELECT status INTO v_status FROM source WHERE source_id p_source_id FOR UPDATE; IF v_status 0 THEN UPDATE source SET status 1 WHERE source_id p_source_id; INSERT INTO registration (patient_id, dept_id, doctor_id, source_id) SELECT p_patient_id, doc.dept_id, doc.doctor_id, p_source_id FROM source s JOIN doctor doc ON s.doctor_id doc.doctor_id WHERE s.source_id p_source_id; SET p_reg_id LAST_INSERT_ID(); UPDATE schedule SET registered_num registered_num 1 WHERE schedule_id (SELECT schedule_id FROM source WHERE source_id p_source_id); COMMIT; SET p_msg 挂号成功; ELSE ROLLBACK; SET p_msg 号源已被占用; END IF; END$$ DELIMITER ;参数与逻辑说明SELECT ... FOR UPDATE 把行锁加上防止两个并发事务同时读到 status0 把同一号源挂出。这是解决超挂问题的数据库层面方案在答辩时讲清楚这一句价值远超会写十个普通查询。p_msg 输出参数用于业务提示p_reg_id 返回新挂号单号。整个流程里 schedule 表的更新是针对排班批次做汇总与 source 表的行锁形成两层保护。调用示例CALL sp_register(1, 10, reg_id, msg); SELECT reg_id, msg;6.3 文档组装顺序与答辩前自测清单课程设计文档建议按下述顺序组织这份顺序与实际设计流程一致导师翻阅时可以按图索骥。需求分析说明业务流程、用户角色和功能需求然后画 E-R 图给出关系模式与范式分析接着是物理建表脚本与数据字典对照最后是核心功能 SQL 与存储过程/视图的展示。数据字典可以放附录但建表 SQL 必须在正文出现。答辩前自测三个问题。第一任意主外键的级联路径能否讲清楚比如删除一个医生会发生什么——建议回答逻辑删除物理上保留历史挂号数据。第二号源并发防超挂的机制能否说透是用唯一约束还是行锁两者分别处在哪个层面。第三能现场演示一条查询展示五张表左右的关联查询而不仅仅是单表 SELECT。这三关过了这门课程设计基本就稳了。我过去在类似项目里反复确认过一件事数据库设计课程设计的高分答案并不需要复杂炫技它需要的是把每个设计决策的前因后果说清楚。每张表的存在都有业务依据、每个冗余都经过取舍、每个约束都能挡下一个真实错误文档的逻辑链条从头到尾连得上这就是一份扎实的课程设计。希望你做完这套方案后不只是拿到一份能交的文档也把建模的思维方式装进自己的工具箱里后面做项目写表时少走这些弯路希望帮到你。本文还有配套的精品资源点击获取
阅读完成 · 觉得有帮助?
咨询建站