☰
医院病房管理系统数据库课设:表结构、索引与事务验收
2026/10/31 2:27:01 网站建设 项目流程

简介:《医院病房管理系统--数据库课设》面向计算机、信息管理等相关专业学生及数据库课程设计参考者,围绕住院病人、病房床位、医护人员与诊疗流程构建业务闭环,涵盖需求分析、ER建模、关系表设计和系统实现,可用于课设复现、答辩演示与数据库综合练习。压缩包共146个文件,以Java源码和class文件为主体,辅以jpg/png界面截图、xml配置、SQL建库脚本和doc说明文档,完整呈现从建表到功能模块落地的过程,包体约5.04MB,目录紧凑便于按模块查阅。已有167人学习,读者可参考病人信息、病房与床位管理、医生排班、费用结算等模块的表结构、主外键与索引设计,也能对照代码理解事务处理、权限控制与数据校验,并借助截图和文档把握系统功能边界与课设文档组织方式。

1. 医院病房管理系统数据库课设:先判断这套源码能跑通什么

答辩前一周拿到一个名为「医院病房管理系统--数据库课设.zip」的压缩包,最怕的不是页面丑,而是表一多就说不清床位、患者、住院记录之间到底谁约束谁。这个资源就是一套围绕病房管理业务展开的数据库课设:核心落在科室、病房、床位、患者、住院登记、医嘱这些实体上,配套建库脚本与业务查询,适合数据库课设冲刺、课程答辩演示,也适合刚接触业务建模的开发者拿来拆表关系。它不是拿来直接交差的万能模板,但能让你在一周内把“床位状态怎么流转、入院出院怎么落库、外键和索引怎么摆”这些答辩高频问题跑通。拿到后先别急着点启动类,先翻建库脚本和连接配置,确认这套库能不能在你本机完整建出来。

2. 病房管理表结构落地:从实体关系到外键、状态字段与索引

表结构不是越多越像课设,答辩老师更愿意听你解释“为什么床位状态不能只靠住院记录反推”。我一般先把业务状态机画出来:床位只有空闲、占用、维修三种稳定状态,住院记录只有在院、出院两种主状态,转床是住院记录换床位并留痕,不是把旧记录删掉。状态机定清楚后,科室、病房、床位三张表的关系才不会绕。下面这段建表 SQL 覆盖核心表,字符集和排序规则按你本机 MySQL 版本调整,5.7 环境把utf8mb4_0900_ai_ci换成utf8mb4_general_ci即可。

-- 建库:统一 utf8mb4,避免患者姓名、诊断文本出现乱码 CREATE DATABASE IF NOT EXISTS ward_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci; USE ward_db; -- 科室:病房的上级组织,课设里不要省,否则床位查询没有归属维度 CREATE TABLE department ( id BIGINT PRIMARY KEY AUTO_INCREMENT, dept_name VARCHAR(50) NOT NULL, location VARCHAR(100) DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_dept_name (dept_name) ) ENGINE=InnoDB; -- 病房:一个科室可以有多个病区/病房,room_type 用来区分普通/隔离等 CREATE TABLE ward ( id BIGINT PRIMARY KEY AUTO_INCREMENT, department_id BIGINT NOT NULL, ward_name VARCHAR(50) NOT NULL, floor_no INT NOT NULL, room_type VARCHAR(20) NOT NULL DEFAULT '普通病房', CONSTRAINT fk_ward_dept FOREIGN KEY (department_id) REFERENCES department(id) ON DELETE RESTRICT ON UPDATE CASCADE ) ENGINE=InnoDB; -- 床位:状态用 ENUM 方便课设演示,生产里更建议 TINYINT + 字典表 CREATE TABLE bed ( id BIGINT PRIMARY KEY AUTO_INCREMENT, ward_id BIGINT NOT NULL, bed_no VARCHAR(20) NOT NULL, status ENUM('FREE','OCCUPIED','MAINTENANCE') NOT NULL DEFAULT 'FREE', version INT NOT NULL DEFAULT 0, CONSTRAINT fk_bed_ward FOREIGN KEY (ward_id) REFERENCES ward(id) ON DELETE RESTRICT ON UPDATE CASCADE, UNIQUE KEY uk_ward_bed (ward_id, bed_no), KEY idx_bed_status (status) ) ENGINE=InnoDB;

这段代码的关键不在字段多少,而在约束顺序:department先建,ward再建,bed最后建,外键才不会报“引用表不存在”。uk_ward_bed保证同一病房里床位号不重复,idx_bed_status是给“查空闲床位”准备的,但只有状态一个列时区分度不高,后面还要补复合索引。version字段不是课设必须,但我建议留着,后面做乐观锁或转床冲突排查时能当后悔药用。ENUM的好处是建表和查询直观,坏处是新增状态要改表结构,课设演示够用,真实项目里通常换成状态字典表。

2.1 患者、住院记录与医嘱:用外键和冗余字段控制查询成本

患者表和住院记录表是整套课设的查询中心。患者用住院号做业务唯一键,住院记录用patient_id和bed_id串起“谁住在哪张床”,出院时间允许为空,状态字段用来区分在院和已出院。转床不要直接改历史,常见做法是新增一条转床日志,记录原床位、新床位、操作时间和原因,答辩时被问“转床怎么追溯”就能直接指表。医嘱表挂在住院记录下,不直接挂患者,因为一次住院可能有多条医嘱,挂错层级后面统计会很难受。

-- 患者:住院号做业务唯一键,身份证等敏感字段课设里可只留脱敏示例 CREATE TABLE patient ( id BIGINT PRIMARY KEY AUTO_INCREMENT, patient_no VARCHAR(30) NOT NULL, patient_name VARCHAR(50) NOT NULL, gender ENUM('M','F','UNKNOWN') NOT NULL DEFAULT 'UNKNOWN', birth_date DATE DEFAULT NULL, phone VARCHAR(20) DEFAULT NULL, created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, UNIQUE KEY uk_patient_no (patient_no) ) ENGINE=InnoDB; -- 住院记录:bed_id 指向当前床位,出院后保留记录,不物理删除 CREATE TABLE admission ( id BIGINT PRIMARY KEY AUTO_INCREMENT, patient_id BIGINT NOT NULL, bed_id BIGINT NOT NULL, attending_doctor_id BIGINT DEFAULT NULL, admission_time DATETIME NOT NULL, discharge_time DATETIME DEFAULT NULL, status ENUM('IN_HOSPITAL','DISCHARGED') NOT NULL DEFAULT 'IN_HOSPITAL', diagnosis VARCHAR(255) DEFAULT NULL, CONSTRAINT fk_adm_patient FOREIGN KEY (patient_id) REFERENCES patient(id) ON DELETE RESTRICT ON UPDATE CASCADE, CONSTRAINT fk_adm_bed FOREIGN KEY (bed_id) REFERENCES bed(id) ON DELETE RESTRICT ON UPDATE CASCADE, KEY idx_adm_patient_status (patient_id, status), KEY idx_adm_bed_status (bed_id, status) ) ENGINE=InnoDB; -- 转床日志:只追加不更新,答辩问追溯时直接查这张表 CREATE TABLE bed_transfer_log ( id BIGINT PRIMARY KEY AUTO_INCREMENT, admission_id BIGINT NOT NULL, from_bed_id BIGINT NOT NULL, to_bed_id BIGINT NOT NULL, transfer_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, reason VARCHAR(200) DEFAULT NULL, CONSTRAINT fk_trans_adm FOREIGN KEY (admission_id) REFERENCES admission(id), CONSTRAINT fk_trans_from FOREIGN KEY (from_bed_id) REFERENCES bed(id), CONSTRAINT fk_trans_to FOREIGN KEY (to_bed_id) REFERENCES bed(id) ) ENGINE=InnoDB;

这里的外键全部用ON DELETE RESTRICT,意思是患者有住院记录时不允许直接删患者,床位被住院记录引用时也不允许直接删床位。课设演示里有人图省事写成级联删除,结果一删患者把住院记录和医嘱全带走了,答辩现场数据对不上,很难圆。idx_adm_patient_status和idx_adm_bed_status是给“查某患者当前住院记录”“查某床位当前占用记录”准备的复合索引,顺序不能反:等值条件在前,状态在后,查询才能稳定命中。诊断文本用VARCHAR(255)是课设尺度,真实病历文本更长,但课设里没必要上TEXT把表撑大。

2.2 索引与视图:把“空闲床位”和“在院患者”固定成查询入口

课设最常被追问的查询有两个:某个病房还有没有空闲床位,当前在院患者名单怎么出。前者如果只建status单列索引,查询会先扫出所有空闲床位再按病房过滤,数据一多就难看;后者如果每次现写多表连接,答辩时容易漏条件。我一般把高频条件做成复合索引,再用视图把“在院患者”固定下来,前端和后端都少写重复 SQL。下面这段索引和视图不追求大而全,够课设演示和追问即可。

-- 复合索引:病房 + 状态 + 床位号,覆盖空闲床位列表的过滤和排序 ALTER TABLE bed ADD KEY idx_bed_ward_status_no (ward_id, status, bed_no); -- 在院患者视图:把 admission、patient、bed、ward 的常用连接固定下来 CREATE OR REPLACE VIEW v_inpatient AS SELECT a.id AS admission_id, p.patient_no, p.patient_name, d.dept_name, w.ward_name, b.bed_no, a.admission_time, a.diagnosis FROM admission a JOIN patient p ON p.id = a.patient_id JOIN bed b ON b.id = a.bed_id JOIN ward w ON w.id = b.ward_id JOIN department d ON d.id = w.department_id WHERE a.status = 'IN_HOSPITAL'; -- 床位使用率视图:按病房统计总床位、占用床位、空闲床位 CREATE OR REPLACE VIEW v_bed_usage AS SELECT w.id AS ward_id, w.ward_name, COUNT(b.id) AS total_beds, SUM(CASE WHEN b.status = 'OCCUPIED' THEN 1 ELSE 0 END) AS occupied_beds, SUM(CASE WHEN b.status = 'FREE' THEN 1 ELSE 0 END) AS free_beds FROM ward w LEFT JOIN bed b ON b.ward_id = w.id GROUP BY w.id, w.ward_name;

复合索引(ward_id, status, bed_no)的顺序是照着查询条件排的:先按病房过滤,再按状态过滤,最后用床位号排序。v_inpatient把在院条件写死在视图里,前端调接口时不用每次拼status='IN_HOSPITAL',少一个漏条件的机会。v_bed_usage用了LEFT JOIN,没有床位的病房也会出现在结果里,统计总床位数不会凭空少一行。注意视图不是性能银弹,它只是把 SQL 固定下来,底层还是走表连接,索引没建对照样慢。

提示:课设答辩里不要只说“我建了索引”,要能说清哪个查询用了哪个索引。打开慢查询或EXPLAIN看一眼,比背索引定义有用。

3. 课设跑通路径:初始化脚本、连接配置与四条业务链路

这套课设能不能跑通,关键不在前端页面多漂亮,而在建库、初始化数据、连接配置、业务事务四步是否按顺序走。我习惯先把库建干净,再灌测试数据,最后启动服务验证“查空闲床位、入院、转床、出院”四条链路。顺序反了,最常见的结果是服务启动了但页面一直转圈,日志里不是连接失败就是表不存在。下面按可复现的顺序拆开,命令和 SQL 都可以直接抄,参数按你本机环境改。

3.1 建库前先定字符集、排序规则和SQL模式

字符集问题在课设里特别容易翻车:建库用utf8、表用默认、连接串又没写编码,患者姓名和诊断里一出现中文就变成问号。我的做法是建库、建表、连接串三处统一utf8mb4,排序规则按本机 MySQL 版本选,不追求跟别人一模一样。SQL 模式也要看一眼,尤其是ONLY_FULL_GROUP_BY,很多床位使用率查询在开发机上能跑,换台机器就报GROUP BY错误。下面这段用来确认环境,不直接改生产配置。

-- 查看当前库和排序规则,确认不是 latin1 或旧 utf8 SHOW VARIABLES LIKE 'character_set_database'; SHOW VARIABLES LIKE 'collation_database'; -- 查看 SQL 模式,重点看是否开启 ONLY_FULL_GROUP_BY SELECT @@sql_mode; -- 如果本机排序规则不支持 utf8mb4_0900_ai_ci,建库时换成: -- CREATE DATABASE ward_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;

第一段查询用来确认当前默认库字符集,第二段看排序规则,第三段看 SQL 模式。ONLY_FULL_GROUP_BY开启时,SELECT里的非聚合列必须出现在GROUP BY里,否则报错。课设里不要为了省事直接关掉这个模式,正确做法是把ward_name这类列补进GROUP BY,或者用聚合函数包起来。排序规则不一致时,连表查询可能出现“字符集不匹配”的报错,统一成utf8mb4后基本能消掉。

3.2 初始化数据:科室、病房、床位和测试患者怎么造

初始化数据不要一上来就插几十条,先造一条完整链路:一个科室、一个病房、两张床位、一个患者、一条在院记录。这样跑入院、转床、出院时能清楚看到每条 SQL 改了哪张表。插入时不要硬编码自增 ID,先用查询拿到 ID 再插子表,否则换台机器自增值不同,脚本直接外键失败。下面这段初始化 SQL 用变量方式写,MySQL 命令行和客户端里都能执行。

-- 初始化科室和病房,先插父表 INSERT INTO department (dept_name, location) VALUES ('内科', 'A座3层'); SET @dept_id = LAST_INSERT_ID(); INSERT INTO ward (department_id, ward_name, floor_no, room_type) VALUES (@dept_id, '内科一病区', 3, '普通病房'); SET @ward_id = LAST_INSERT_ID(); -- 造两张床位,一空闲一维修,方便演示状态过滤 INSERT INTO bed (ward_id, bed_no, status) VALUES (@ward_id, '301-1', 'FREE'), (@ward_id, '301-2', 'MAINTENANCE'); SET @bed_free_id = (SELECT id FROM bed WHERE ward_id = @ward_id AND bed_no = '301-1'); -- 造一个测试患者和一条在院记录,入院时间用当前时间 INSERT INTO patient (patient_no, patient_name, gender, birth_date) VALUES ('ZY2024001', '测试患者甲', 'M', '1990-01-01'); SET @patient_id = LAST_INSERT_ID(); INSERT INTO admission (patient_id, bed_id, admission_time, status, diagnosis) VALUES (@patient_id, @bed_free_id, NOW(), 'IN_HOSPITAL', '待查'); UPDATE bed SET status = 'OCCUPIED' WHERE id = @bed_free_id;

这段脚本先插科室,用LAST_INSERT_ID()拿自增 ID,再插病房和床位,最后插患者和住院记录。SET @bed_free_id用查询拿床位 ID,避免硬编码。最后一步把床位状态改成OCCUPIED,和住院记录保持一致。真实初始化时可以把这几步包进事务,但课设演示分步执行更直观,出错也能定位到哪张表。测试患者姓名用“测试患者甲”这类虚构代称,不要填真实信息。

3.3 连接配置与启动顺序:先库后服务,再验四条链路

如果压缩包里是 Spring Boot 那套结构,连接配置通常在application.properties或application.yml里;如果是别的框架,找对应的数据源配置,参数含义一样。连接串里我一般会显式写字符集、时区、是否用 SSL,避免本机 MySQL 8 默认认证插件和旧驱动打架。下面这段配置只改库名、用户名、密码和端口,其他参数按你本机情况保留。

# 数据源连接:库名、时区、字符集按本机环境改 spring.datasource.url=jdbc:mysql://127.0.0.1:3306/ward_db?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai&useSSL=false&allowPublicKeyRetrieval=true spring.datasource.username=root spring.datasource.password=你的密码 spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver

useUnicode=true和characterEncoding=utf8用来保证中文参数不乱码,serverTimezone=Asia/Shanghai解决时间差 8 小时,useSSL=false在本地课设环境省掉证书配置,allowPublicKeyRetrieval=true是给 MySQL 8 默认认证方式留的口子。启动顺序建议固定成:先确认 MySQL 服务在跑,再执行建库建表脚本,再灌初始化数据,最后启动后端和前端。页面打不开时先看后端日志有没有“Table 'ward_db.xxx' doesn't exist”,有就说明脚本没跑完,不要先怀疑前端。

3.4 四条业务链路的SQL验证:空闲床位、入院、转床、出院

业务链路不要只靠页面点,页面点不通时你分不清是接口问题还是 SQL 问题。我习惯先用 SQL 把四条链路各跑一遍:查空闲床位、入院占床、转床、出院释放。入院和转床必须放在事务里,并且对床位行加锁,否则两个人同时点“入院”可能把同一张床分给两个患者。下面这段用事务和FOR UPDATE演示,参数位置用?表示,实际执行时换成具体 ID。

-- 链路1:查指定病房的空闲床位,按床位号排序 SELECT id, bed_no FROM bed WHERE ward_id = ? AND status = 'FREE' ORDER BY bed_no; -- 链路2:入院占床,必须在事务里锁行再改状态 START TRANSACTION; SELECT id, status FROM bed WHERE id = ? AND status = 'FREE' FOR UPDATE; -- 应用层判断查询结果为空则回滚,不要继续更新 UPDATE bed SET status = 'OCCUPIED' WHERE id = ? AND status = 'FREE'; INSERT INTO admission (patient_id, bed_id, admission_time, status) VALUES (?, ?, NOW(), 'IN_HOSPITAL'); COMMIT; -- 链路3:转床,锁新旧两张床,更新住院记录并写转床日志 START TRANSACTION; SELECT id, status FROM bed WHERE id IN (?, ?) FOR UPDATE; UPDATE bed SET status = 'FREE' WHERE id = ? AND status = 'OCCUPIED'; UPDATE bed SET status = 'OCCUPIED' WHERE id = ? AND status = 'FREE'; UPDATE admission SET bed_id = ? WHERE id = ? AND status = 'IN_HOSPITAL'; INSERT INTO bed_transfer_log (admission_id, from_bed_id, to_bed_id, reason) VALUES (?, ?, ?, '病情调整'); COMMIT; -- 链路4:出院,更新住院记录并释放床位 START TRANSACTION; SELECT bed_id FROM admission WHERE id = ? AND status = 'IN_HOSPITAL' FOR UPDATE; UPDATE admission SET status = 'DISCHARGED', discharge_time = NOW() WHERE id = ? AND status = 'IN_HOSPITAL'; UPDATE bed SET status = 'FREE' WHERE id = ? AND status = 'OCCUPIED'; COMMIT;

链路 1 的查询走idx_bed_ward_status_no,条件是病房和状态,排序字段也在索引里,执行计划会比较干净。链路 2 的FOR UPDATE锁住候选床位行,UPDATE再带一次status='FREE'条件,应用层要检查受影响行数,0 行就回滚,不能继续插住院记录。链路 3 先锁新旧床位,再改状态、改住院记录、写日志,顺序不要乱:先释放旧床再占新床,中间失败会留下空档,所以事务必须包住。链路 4 先查住院记录拿床位 ID,再改记录和床位,status条件防止重复出院。四条链路都能用 SQL 跑通,页面联调时才不会把数据库问题误判成前端问题。

注意:FOR UPDATE必须在事务里用,自动提交模式下锁一下就释放,等于没锁。课设演示时把autocommit关掉或显式START TRANSACTION。

4. 常见问题排查:答辩前最容易翻车的五类数据库坑

课设翻车大多不是算法难,而是脚本执行顺序、字符集、事务边界这些细节没对齐。下面五类问题是我在类似病房管理课设里见过最多的,按“现象、原因、解决”拆开写,照着排查能省掉不少答辩前现猜的时间。血泪经验是:页面报错先看后端日志,后端日志先看 SQL 异常,不要一上来重装环境。

4.1 建库与脚本执行阶段的坑

第一类现象是执行建表脚本时报Cannot add foreign key constraint,或者提示引用表不存在。原因通常是建表顺序反了,子表先于父表执行,或者表引擎不是 InnoDB,外键根本不生效。解决办法是把脚本按department → ward → bed → patient → admission → bed_transfer_log的顺序拆开执行,建表语句显式写ENGINE=InnoDB,执行前先DROP DATABASE IF EXISTS ward_db清掉半成品库。不要在一个大脚本里混着建库、建表、插数据,出错时很难定位到第几条。

第二类现象是患者姓名、诊断文本存进去变成问号,或者查询条件带中文查不到。原因一般是建库字符集、表字符集、连接串编码三处不一致,常见组合是库用utf8、连接串没写编码、客户端默认latin1。解决办法是建库和表统一utf8mb4,连接串加useUnicode=true&characterEncoding=utf8,执行脚本的客户端也确认使用 UTF-8。已经建好的表可以用ALTER TABLE ... CONVERT TO CHARACTER SET utf8mb4修,但外键列要一起改,否则又会报字符集不匹配。

第三类现象是床位使用率查询在本机能跑,换台机器报Expression #2 of SELECT list is not in GROUP BY clause。原因是开了ONLY_FULL_GROUP_BY,查询里ward_name没有出现在GROUP BY里。解决办法是把ward_name补进GROUP BY,或者用ANY_VALUE(ward_name)包起来。课设里不建议直接SET sql_mode=''关掉模式,答辩老师一问“为什么关”很难解释。正确写法是让非聚合列出现在分组条件里,语义也更清楚。

-- 错误写法在 ONLY_FULL_GROUP_BY 下会报错 SELECT w.id, w.ward_name, COUNT(b.id) FROM ward w LEFT JOIN bed b ON b.ward_id = w.id GROUP BY w.id; -- 修正:把 ward_name 补进 GROUP BY SELECT w.id, w.ward_name, COUNT(b.id) FROM ward w LEFT JOIN bed b ON b.ward_id = w.id GROUP BY w.id, w.ward_name;

这段对比不是为了背语法,而是让你在报错时知道往哪改。GROUP BY w.id在部分数据库里能跑,是因为它们对非聚合列做了隐式处理,MySQL 开启严格模式后不认。把ward_name补进去后,分组语义明确,换环境也不容易翻车。

4.2 业务联调与数据一致性阶段的坑

第四类现象是同一张床位被分给两个在院患者,页面上看两张住院记录都指向同一个bed_id。原因通常是“先查空闲再更新”没有放在事务里,两个请求同时查到FREE,然后各自更新成功。解决办法是入院和转床都走事务,用SELECT ... FOR UPDATE锁床位行,更新时再带status='FREE'条件,并检查受影响行数。应用层拿到 0 行就回滚,不要继续插住院记录。课设演示时可以用两个命令行窗口同时执行,能直观看到锁的作用。

第五类现象是入院时间、出院时间比实际少 8 小时,或者前端显示的时间和数据库里差一截。原因一般是连接串没配时区,或者数据库字段用TIMESTAMP而服务器时区不一致。解决办法是连接串加serverTimezone=Asia/Shanghai,业务时间字段统一用DATETIME,插入时用NOW()或应用层传同一时区的时间。已经存进去的脏数据不要直接批量改,先查出来对照业务时间再修,避免把正常记录改坏。

-- 排查时间差:对比数据库当前时间和业务表里的入院时间 SELECT NOW() AS db_now, admission_time, discharge_time FROM admission WHERE status = 'IN_HOSPITAL' ORDER BY admission_time DESC LIMIT 10; -- 排查床位与住院记录打架:占用床位却没有在院记录 SELECT b.id, b.bed_no, b.status FROM bed b LEFT JOIN admission a ON a.bed_id = b.id AND a.status = 'IN_HOSPITAL' WHERE b.status = 'OCCUPIED' AND a.id IS NULL;

第一段查询用来确认数据库当前时间和业务时间是否在同一时区,第二段查“床位显示占用但找不到在院记录”的脏数据。出现这种数据时不要急着改床位状态,先确认是不是出院事务只更新了住院记录没释放床位,或者转床事务中途失败。把两条 SQL 存成排查脚本,答辩前跑一遍,比现场翻日志快。

5. 用EXPLAIN和一致性SQL验收课设:让表结构经得起追问

课设做到能跑只是及格线,答辩老师随便追问一句“空闲床位查询走索引了吗”“床位占用和住院记录对不上怎么办”,就能看出表结构是不是真的落地。我一般会在演示前做两件事:用EXPLAIN看核心查询的执行计划,用一致性 SQL 扫一遍床位状态和住院记录是否打架。下面这条查询对应“某病房空闲床位列表”,重点看type是不是ref或range,key有没有命中idx_bed_ward_status_no,rows是不是明显小于全表。

EXPLAIN SELECT id, bed_no FROM bed WHERE ward_id = 1 AND status = 'FREE' ORDER BY bed_no;

如果key为空或者type=ALL,说明复合索引没命中,先检查索引列顺序是不是(ward_id, status, bed_no),再检查查询条件有没有对索引列做函数运算。Extra里出现Using filesort不一定是错,但课设数据量小的时候最好让它消失,排序字段在索引里就能省一次排序。下面两条一致性 SQL 分别查“床位占用但没有在院记录”和“在院记录但床位不是占用”,答辩前跑一遍,结果为空再演示。

-- 床位占用但没有对应在院记录 SELECT b.id, b.bed_no FROM bed b LEFT JOIN admission a ON a.bed_id = b.id AND a.status = 'IN_HOSPITAL' WHERE b.status = 'OCCUPIED' AND a.id IS NULL; -- 在院记录指向的床位不是占用状态 SELECT a.id, a.bed_id, b.status FROM admission a JOIN bed b ON b.id = a.bed_id WHERE a.status = 'IN_HOSPITAL' AND b.status <> 'OCCUPIED';
答辩追问验证动作预期结果
空闲床位查询为什么快跑EXPLAIN看key和rows命中idx_bed_ward_status_no
转床后旧床为什么释放查bed_transfer_log和两张床位状态旧床FREE,新床OCCUPIED
出院后记录还在不在查admission.status和discharge_time状态DISCHARGED,时间非空
床位和住院记录会不会打架跑上面两条一致性 SQL两条结果都为空

从那以后我每次拿到数据库课设包,都先跑一遍一致性 SQL 再改页面,不然演示时一个床位显示两个患者,现场很难圆。这套医院病房管理系统数据库课设的价值不在代码量,而在你能把表关系、事务边界和索引验证讲清楚;需要原包的,按标题「医院病房管理系统--数据库课设.zip」去资源页取,解压后先跑建表脚本,再按第 3 章四条链路验一遍。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询