多彩编程 多彩编程MZPH · CODE BLOG
ARTICLE DETAIL

文章详情

深耕前端与后端开发技术的一线实战笔记与踩坑复盘。

医院病房管理系统数据库课设实战:表结构、触发器与存储过程

医院病房管理系统数据库课设实战:表结构、触发器与存储过程 简介面向数据库课程设计的医院病房管理系统项目涵盖ER建模、关系表设计、Java前后端开发及住院业务流程实现并涉及关系数据库理论与软件工程方法可作为高校学生完成课设或毕业设计的参考样板。压缩包共146个文件以Java源码37个.java、编译产物59个.class、数据库SQL脚本及工程配置文件为主并含界面截图与说明文档整体仅5.04MB便于下载与本地部署。已有165人学习下载适合正在规划数据库课设或需要完整项目骨架的读者参考。资源提供了从病人入院登记、病房床位分配、医生排班到费用结算的完整业务模块通过源码可学习主键外键设计、事务处理与权限控制等关键实践附带的SQL脚本和项目文档能帮助快速导入数据并理解整体结构节省从零搭建的时间。1. 医院病房管理系统数据库课设资源先看它解决了什么第一次看到「医院病房管理系统——数据库课设.zip」这个文件名时我以为是又一个只能跑通增删改查的练习小项目。真正拆开才发现它把患者、医生、病房、床位、医嘱、护理记录和费用全部串进了同一套关系模型里用视图、触发器、存储过程把一张张看似孤立的表连成了完整业务流。A同学答辩前一周拿到这套资源按顺序执行建库脚本、导入示例数据、再照着报告骨架把设计思路讲清楚最后顺利过了。这套资源适合正在做数据库课程设计的学生、想快速复现一套完整业务库的开发新人也适合手里有差不多的课设需求、但不知道表和表之间到底该怎么串的人。它能帮你省掉从零画ER图、纠结字段类型、写不出带事务的存储过程这些最耗时的环节。2. 表结构设计从ER模型到八张业务表的落地细节课设答辩时老师很少问“你用了几个表”问得最多的是“为什么这张表要这么设计”。所以拿到资源后别急着执行SQL先把它拆成模块看。这套医院病房管理系统在数据层面可以划分为基础档案、诊疗业务、费用统计三大块每一块对应两到三张表。2.1 模块边界与核心表职责先从资源附带的设计文档里把表结构清单抄出来对照职责看模块表名核心职责典型字段基础档案doctor医生信息科室、职称、工号基础档案nurse护士信息所属病区、排班病区管理ward病房信息病房类型、总床位、可用床位病区管理bed病床信息所属病房、床位号、状态诊疗业务patient患者主档姓名、性别、入院时间、主治医生诊疗业务medical_order医嘱记录医嘱内容、开立时间、执行状态护理记录nursing_record护理操作记录体温、血压、护理备注费用统计charge_record费用明细费用项目、金额、结算状态这个划分是课设里比较标准的答案医生、护士分开建表而不是塞进同一张“员工表”是因为两类人员的属性差异大后续扩展排班和护理排班都方便。患者表单独建不把个人信息重复写进医嘱表符合第三范式。2.2 建表脚本字段类型、主键与字符集怎么选直接把资源里的核心建表语句整理出来我一般会改写成下面这样再上机执行CREATE DATABASE IF NOT EXISTS hospital_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; USE hospital_db; CREATE TABLE ward ( ward_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 病房ID, ward_no VARCHAR(20) NOT NULL UNIQUE COMMENT 病房编号, ward_type VARCHAR(20) NOT NULL DEFAULT 普通病房 COMMENT 病房类型, total_beds INT NOT NULL DEFAULT 4 COMMENT 总床位数, available_beds INT NOT NULL DEFAULT 4 COMMENT 可用床位数 ) ENGINEInnoDB COMMENT病房表; CREATE TABLE bed ( bed_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 病床ID, ward_id INT NOT NULL COMMENT 所属病房ID, bed_no VARCHAR(10) NOT NULL COMMENT 床位号, status VARCHAR(20) NOT NULL DEFAULT AVAILABLE COMMENT AVAILABLE/OCCUPIED, patient_id INT DEFAULT NULL COMMENT 当前占用患者ID空则为空床, CONSTRAINT uk_ward_bed UNIQUE (ward_id, bed_no), CONSTRAINT fk_bed_ward FOREIGN KEY (ward_id) REFERENCES ward(ward_id) ) ENGINEInnoDB COMMENT病床表; CREATE TABLE patient ( patient_id INT AUTO_INCREMENT PRIMARY KEY COMMENT 患者ID, name VARCHAR(50) NOT NULL COMMENT 患者姓名, gender VARCHAR(4) NOT NULL COMMENT 性别男/女, age INT NOT NULL COMMENT 年龄, doctor_id INT NOT NULL COMMENT 主治医生, admit_date DATE NOT NULL COMMENT 入院日期, discharge_date DATE DEFAULT NULL COMMENT 出院日期, bed_id INT DEFAULT NULL COMMENT 当前床位ID, CONSTRAINT fk_patient_doctor FOREIGN KEY (doctor_id) REFERENCES doctor(doctor_id), CONSTRAINT fk_patient_bed FOREIGN KEY (bed_id) REFERENCES bed(bed_id) ) ENGINEInnoDB COMMENT患者主档表;这段脚本最值得抄的是三个取舍。第一字符集统一用utf8mb4不是utf8因为数据库课程设计里经常有人把“患者姓名”做成表情符号测试utf8存不了四字节字符插入直接报错。第二gender用VARCHAR(4)而不是 MySQL 的ENUM(M,F)虽然 ENUM 省空间但课设答辩时老师常问“如果哪天需要存第三类性别怎么办”改 ENUM 的代价比改字符串大得多。第三bed表加了patient_id可空列用来记录当前占用关系。这是一种冗余设计但能让病床状态查询少一次 JOIN答辩时可以主动讲出“这是用空间换查询性能”。需要注意建表顺序。patient引用了doctor和bedbed引用了ward所以必须先建ward、doctor再建bed最后建patient。如果你直接整体导入还报外键错误通常就是脚本里的建表顺序没排好。2.3 外键约束与级联删除别给病床留反锁外键是课设的高频考点也是最容易翻车的地方。资源里默认用的是RESTRICT也就是子表还有引用时父表禁止删除。-- 删除一个还被病床引用的病房会报外键错误 DELETE FROM ward WHERE ward_id 1; -- 先删除该病房下所有病床再删病房才能成功 DELETE FROM bed WHERE ward_id 1; DELETE FROM ward WHERE ward_id 1;这里有个经验病房和病床不要设置ON DELETE CASCADE。看起来级联删除很省事但真实场景里病床可能关联过大量历史医嘱和护理记录一旦级联删除会把整条业务链上的数据全部抹掉。A同学第一次把课设里的外键全部改成 CASCADE演示“删除病房”功能时患者基本档案连带没了当场被老师指出不符合医疗数据保留规范。正确做法是在 app 层先做“停用”操作给病房加一个is_active字段把状态置为0而不是物理删除。外键还有一个隐性要求两张表之间的关联字段类型必须完全一致。比如ward.ward_id是INT那bed.ward_id就不能写成BIGINT或INT UNSIGNED否则 MySQL 会在建表时报“无法创建外键约束”这个报错信息很玄学不仔细看类型根本发现不了。3. 环境与初始化让建表脚本一次跑通的三个前置条件很多同学拿到课设包后第一句话是“脚本报错了”。多数时候不是脚本问题是环境没对齐。这套资源的初始化脚本按 MySQL 语法写的下面的流程就是我把脚本从报错调到跑通的全过程。3.1 版本与连接参数先确认本地数据库版本。用以下命令查看mysql -u root -p SELECT VERSION();资源里的脚本兼容 MySQL 5.7 和 8.x但有个关键差异要提前处理。MySQL 8.x 默认认证插件是caching_sha2_password如果你用的是较老的图形客户端或驱动连接时经常报“Authentication plugin cannot be loaded”。常见做法是给使用的账号改回旧的认证方式ALTER USER rootlocalhost IDENTIFIED WITH mysql_native_password BY 你的密码; FLUSH PRIVILEGES;这条命令只影响指定的账号不会破坏数据库本身。跑完再连接就正常了。连接参数同样要统一字符集。JDBC 连接串里至少带上这几个参数characterEncodingutf8、useSSLfalse、serverTimezoneAsia/Shanghai。少了characterEncoding中文写进库后再查出来就是问号后面所有作业等于白做。3.2 初始化脚本的执行路径这套资源里的脚本按编号排列标准执行顺序是建库、建表、插基础数据、插模拟业务数据。我习惯用命令行按顺序执行错误信息看得最清楚# 创建数据库并指定字符集 mysql -u root -p -e CREATE DATABASE IF NOT EXISTS hospital_db DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci; # 导入表结构 mysql -u root -p hospital_db scripts/01_schema.sql # 导入基础字典数据 mysql -u root -p hospital_db scripts/02_base_data.sql # 导入模拟业务数据 mysql -u root -p hospital_db scripts/03_sample_data.sql这里有一个必须遵守的顺序原则先导表结构再导数据。如果先执行03_sample_data.sql里面插入的patient记录引用了不存在的表直接报“Table not found”。另一个容易忽略的点是如果中间某一条插入语句因为外键问题失败MySQL 默认不会回滚整个文件它会把报错语句跳过继续执行后面的语句。所以导入完成后不能只看“有没有报错”还要抽查数据量。我习惯在三个脚本全部执行后再跑一条验证语句确认关键表有数据SELECT (SELECT COUNT(*) FROM ward) AS ward_cnt, (SELECT COUNT(*) FROM bed) AS bed_cnt, (SELECT COUNT(*) FROM patient) AS patient_cnt, (SELECT COUNT(*) FROM medical_order) AS order_cnt;只要四行数据都大于 0说明核心链路已经通了。如果medical_order是 0通常是03_sample_data.sql里这段业务数据没有命中正确的doctor_id外键校验把它拦下来了。3.3 用示例数据验证表关系建好表、导完数据接下来的关键动作是验证“病床与患者”的关联是否正确。我自己验证时会手动模拟一次入院而不是直接相信示例数据-- 找一个状态为 AVAILABLE 的病床 SELECT bed_id, ward_id, bed_no FROM bed WHERE status AVAILABLE LIMIT 1; -- 手动插入一名患者 INSERT INTO patient (name, gender, age, doctor_id, admit_date, bed_id) VALUES (测试患者A, 男, 58, 1, CURDATE(), 12); -- 同步占用病床 UPDATE bed SET status OCCUPIED, patient_id LAST_INSERT_ID() WHERE bed_id 12;注意LAST_INSERT_ID()只在同一个连接里有效。如果你先用图形工具插入了患者再开一个命令行窗口去更新病床拿到的值是 0床位关联就错了。正确做法是 INSERT 和 UPDATE 在同一个会话里连续执行或者先查出真实patient_id再手动填进去。顺手提一句UPDATE语句的坑如果不加WHERE bed_id 12会把整张病床表全部改成占用状态。课设演示时这个错误特别容易发生因为身上压力一大手一抖就少写了条件。从那以后我每次写 UPDATE 都先写 WHERE再回去补 SET养成习惯后基本没再翻过车。4. 核心功能SQL视图、触发器与存储过程这样抄作业课设能不能拿高分主要看这一层有没有东西。光会建表和增删改查只算及格视图、触发器、存储过程和权限控制才是答辩时的亮点。这套资源在这块给了比较完整的实现我的建议是别整段复制改完参数再上机。4.1 视图病房占用情况与患者费用汇总视图是给“反复使用的复杂查询”准备的。比如病房占用情况它需要关联病房表、病床表、患者表每次手写 JOIN 很容易漏而且容易写出笛卡尔积。资源里用视图把这层查询封装成一张“虚拟表”。CREATE OR REPLACE VIEW v_ward_occupancy AS SELECT w.ward_id, w.ward_no, w.ward_type, w.total_beds, w.available_beds, COUNT(b.bed_id) AS current_beds, SUM(CASE WHEN b.status OCCUPIED THEN 1 ELSE 0 END) AS occupied_beds FROM ward w LEFT JOIN bed b ON w.ward_id b.ward_id GROUP BY w.ward_id, w.ward_no, w.ward_type, w.total_beds, w.available_beds;查询时直接SELECT * FROM v_ward_occupancy WHERE ward_no 301就能拿到一个病房的动态占用数。这个视图有三个要点。第一用LEFT JOIN而不是INNER JOIN否则一间空病房没有病床会被过滤掉统计结果就少了数据。第二GROUP BY必须包含w.ward_id, w.ward_no等所有非聚合列MySQL 默认允许字段少写但查询结果是不确定的。第三CASE WHEN配合SUM是数“满足条件的行数”的标准做法比COUNT(IF(...))更好读。费用汇总视图同理它把费用明细表和结算状态关联起来CREATE OR REPLACE VIEW v_patient_bill AS SELECT p.patient_id, p.name, COUNT(c.charge_id) AS charge_count, SUM(CASE WHEN c.status UNPAID THEN c.amount ELSE 0 END) AS unpaid_amount, SUM(c.amount) AS total_amount FROM patient p LEFT JOIN charge_record c ON p.patient_id c.patient_id GROUP BY p.patient_id, p.name;这个视图直接回答“某个患者现在还欠多少钱”。UNPAID的统计放在 CASE 里而不是WHERE条件中是为了保留已缴费记录方便演示总费用和欠费金额两个指标。4.2 触发器病房可住床位自动扣减触发器属于“让数据库主动干活”的设计。病房表里有total_beds和available_beds两个字段如果每次状态变更都靠应用代码手动更新难免有人忘记。资源里用触发器自动维护这两个字段。DELIMITER $$ CREATE TRIGGER trg_ward_beds_after_bed_update AFTER UPDATE ON bed FOR EACH ROW BEGIN IF NEW.status OCCUPIED AND OLD.status AVAILABLE THEN UPDATE ward SET available_beds available_beds - 1 WHERE ward_id NEW.ward_id; END IF; END$$ DELIMITER ;触发器逻辑是当bed表某一行从AVAILABLE改成OCCUPIED就把所属病房的available_beds减一反过来从占用改成空床就加一。写这个触发器最容易踩的坑是递归更新。如果你在AFTER UPDATE触发器里再次 UPDATEbed表会再次触发同一个触发器形成无限嵌套直接报“Cant update table in stored function/trigger because it is already used by a statement”。正确做法是只更新ward表因为ward表的变化不会再触发bed表的触发器。另外注意UPDATE ward语句必须带WHERE ward_id NEW.ward_id这里的NEW指当前被修改的那行病床数据不是整个触发器作用域。如果你买的资源里同时存在“更新病床状态触发器”和“入院存储过程”一定要测试它们的执行顺序。存储过程执行时第一步UPDATE bed SET statusOCCUPIED就会激发这个触发器触发器的 UPDATE 和存储过程后续的 COMMIT 处在同一事务里任何一个环节出错整笔入院操作都会回滚。4.3 存储过程一次入院的完整事务存储过程是课设里最能体现水平的部分。它能把“查床位、插患者、占床位”三步操作打包成一个完整事务中间任何一步失败都回滚。资源里的sp_admit_patient实现得很典型DELIMITER $$ CREATE PROCEDURE sp_admit_patient( IN p_name VARCHAR(50), IN p_gender VARCHAR(4), IN p_age INT, IN p_doctor_id INT, IN p_bed_id INT, OUT p_patient_id INT ) BEGIN DECLARE v_bed_status VARCHAR(20); -- 出现任何异常直接回滚 DECLARE EXIT HANDLER FOR SQLEXCEPTION BEGIN ROLLBACK; RESIGNAL; END; START TRANSACTION; -- 锁定病床行防止并发下同一张床被分配两次 SELECT status INTO v_bed_status FROM bed WHERE bed_id p_bed_id FOR UPDATE; IF v_bed_status AVAILABLE THEN SIGNAL SQLSTATE 45000 SET MESSAGE_TEXT 床位不可用; END IF; INSERT INTO patient (name, gender, age, doctor_id, admit_date, bed_id) VALUES (p_name, p_gender, p_age, p_doctor_id, CURDATE(), p_bed_id); SET p_patient_id LAST_INSERT_ID(); UPDATE bed SET status OCCUPIED, patient_id p_patient_id WHERE bed_id p_bed_id; COMMIT; END$$ DELIMITER ;调用示例CALL sp_admit_patient(测试患者B, 女, 29, 1, 13, new_patient_id); SELECT new_patient_id;这段存储过程的几个参数需要解释IN是输入参数OUT是输出参数输出的p_patient_id用来让应用层知道新患者的主键SIGNAL SQLSTATE 45000是主动抛出业务异常45000是 MySQL 专门留给用户自定义异常的代码“EXIT HANDLER”用的是SQLEXCEPTION表示任何 SQL 语句报错都会进入回滚逻辑。最关键的语句是SELECT ... FOR UPDATE。如果没有它两个并发请求可能同时读取到AVAILABLE然后同时插入两个患者到同一张病床。加上行锁后第二个请求会等待第一个事务提交这就是数据库课设里“事务隔离”的现场演示材料答辩时主动讲出来非常加分。4.4 权限脚本按角色最小授权权限控制是很多课设不做的内容但这套资源给了比较完整的模型它把数据库用户分成管理员、医生、护士三个角色。医生只能读患者档案和写医嘱护士只能写护理记录管理员才有全部权限。-- 创建三个业务账号 CREATE USER IF NOT EXISTS admin_userlocalhost IDENTIFIED BY Admin123; CREATE USER IF NOT EXISTS doctor_userlocalhost IDENTIFIED BY Doctor123; CREATE USER IF NOT EXISTS nurse_userlocalhost IDENTIFIED BY Nurse123; -- 管理员所有库权限 GRANT ALL PRIVILEGES ON hospital_db.* TO admin_userlocalhost; -- 医生患者与医嘱的读写 GRANT SELECT, INSERT, UPDATE ON hospital_db.patient TO doctor_userlocalhost; GRANT SELECT, INSERT, UPDATE ON hospital_db.medical_order TO doctor_userlocalhost; GRANT SELECT ON hospital_db.v_ward_occupancy TO doctor_userlocalhost; -- 护士只读写护理记录费用只读 GRANT SELECT, INSERT, UPDATE ON hospital_db.nursing_record TO nurse_userlocalhost; GRANT SELECT ON hospital_db.v_patient_bill TO nurse_userlocalhost; FLUSH PRIVILEGES;这里要强调的是权限粒度。医疗数据里护士不应该看到费用明细医生不应该写护理记录这种细粒度授权正好呼应了“权限最小化”原则。答辩时如果老师问“为什么护士看不到患者费用”你可以直接回答“护理角色没有费用相关表的授权这种设计避免越权操作”。另外记得GRANT之后必须FLUSH PRIVILEGES才能在新的连接里生效这个FLUSH不是重载配置而是让 MySQL 重新读一遍权限表。5. 避坑指南课设里最容易翻车的五个场景这章是我每次带着学生调这套资源时总结出来的高频坑。每一条都来自实际运行报错或答辩现场的尴尬时刻按“现象、原因、解决”三段写清楚。5.1 中文写入后全部变成问号现象用图形工具导入02_base_data.sql后科室名、护士姓名显示成“???”但表结构里的中文注释正常。原因数据库、表、连接三个层的字符集不一致。最常见的是客户端连接时用了latin1即使库表建成了utf8mb4写入时也会被转成乱码。解决连接命令加--default-character-setutf8mb4然后在会话里执行SET NAMES utf8mb4。已经乱码的数据只能先删除再重新导入不要试图用 UPDATE 修复那基本是浪费时间。血泪经验建库时一条DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci就能避开 90% 的乱码问题。5.2 触发器更新自身表导致执行失败现象触发器逻辑是“病房可用床位减一”但每次 UPDATEbed表后系统报错提示无法在触发器中更新bed表整笔操作直接回滚。原因我一开始把UPDATE ward错误写成了UPDATE bed比如想通过触发器同步bed的另一个字段。MySQL 不允许在触发器里再次操作自身表误认为触发了无限循环。解决把自我更新改成SET NEW.字段 值。特别注意NEW在 BEFORE 型触发器里可以直接改值但在 AFTER 型触发器里NEW只读。跨表更新时务必确认 UPDATE 语句操作的不是触发器自身所在的表。这个规则没有例外遇到报错先检查触发体里有没有出现和 FOR EACH ROW 相同的表名。5.3 存储过程中途提交导致事务失去作用现象sp_admit_patient在“插患者”之后、更新病床之前某个语句失败调用结束后患者记录居然留在数据库里了。原因存储过程体内某处写了COMMIT或者在调用存储过程前没有执行SET AUTOCOMMIT0。MySQL 默认每一条 INSERT 自带提交事务保护根本生效不了。解决检查存储过程体COMMIT 只能出现在最后一个语句事务开始前执行SET AUTOCOMMIT0异常处理部分只保留ROLLBACK和RESIGNAL不要在 HANDLER 里写 COMMIT。改完后用“人为制造错误”验证故意传入一个已被占用的p_bed_id看看患者是否还被写入如果写完被回滚说明事务生效了。5.4 删除病房时提示外键约束失败现象病房没患者了想删掉一间空病房但 DELETE 报错“Cannot delete or update a parent row”。原因这张病房下还有病床记录病床表的外键引用了病房主键。即使病床状态是 AVAILABLE也仍然是一条物理存在的子记录RESTRICT 约束阻止删除。解决先删病床再删病房顺序不能反过来。更符合业务的是不做物理删除给ward加is_active字段删的时候执行UPDATE ward SET is_active 0 WHERE ward_id ?所有引用关系的完整性都不会被破坏。这个点也是答辩时的加分项主动说明“医疗数据不能物理删只能逻辑删”。5.5 视图统计结果比预期大一倍现象查询v_patient_bill时发现某个患者的费用总额是实际金额的两倍。原因patient表 LEFT JOINcharge_record之后如果charge_record里同一患者有多条记录JOIN 结果行数会翻倍此时如果SUM(c.amount)没有把重复行考虑进去金额自然翻倍。解决先做子查询聚合再关联患者主表CREATE OR REPLACE VIEW v_patient_bill_ok AS SELECT p.patient_id, p.name, c.charge_count, COALESCE(c.unpaid_amount, 0) AS unpaid_amount, COALESCE(c.total_amount, 0) AS total_amount FROM patient p LEFT JOIN ( SELECT patient_id, COUNT(*) AS charge_count, SUM(CASE WHEN status UNPAID THEN amount ELSE 0 END) AS unpaid_amount, SUM(amount) AS total_amount FROM charge_record GROUP BY patient_id ) c ON p.patient_id c.patient_id;这个问题的根因是“先 JOIN 后聚合”和“先聚合后 JOIN”的差别。视图写法里优先选择后者统计结果才稳定。另外外连接要用COALESCE把 NULL 转成 0否则视图里会显示空白金额答辩时容易被追问。6. 进阶验证以“可答辩”为标准做四步收尾自检资源里数据脚本导入完成、功能能跑只算完成了一半。答辩时老师会现场点几个操作所以我把“能跑”升级成“能当场演示不出错”。我现在每次拿到这类课设资源都会强制走一遍四步自检。第一步是“脚本从头跑到尾”。删库重建按01_schema.sql、02_base_data.sql、03_sample_data.sql的顺序重新执行一遍全程记录错误输出。如果第二次执行还有报错说明脚本幂等性不够要么加了没有IF NOT EXISTS的建表语句要么插入了重复主键立刻改掉。第二步是“业务链路连测”。按真实业务流程走一遍先查空床然后CALL sp_admit_patient写入一个测试患者再给患者开一条医嘱最后执行出院。出院这一步我一般会验证触发器是否同步更新了病房可用床位-- 入院前查询病房可用床位 SELECT ward_id, available_beds FROM ward WHERE ward_id 1; -- 入院后再次查询 SELECT w.available_beds, SUM(CASE WHEN b.status OCCUPIED THEN 1 ELSE 0 END) AS actual_occupied FROM ward w JOIN bed b ON w.ward_id b.ward_id WHERE w.ward_id 1 GROUP BY w.ward_id, w.available_beds;两边数值对得上说明触发器没漏执行。第三步是“执行计划检查”。用EXPLAIN看核心查询是否走索引EXPLAIN SELECT * FROM medical_order WHERE patient_id 5;如果type列显示ALL说明这条查询没走索引在patient_id上补一个普通索引即可。别小看这一步老师现场随机报一个患者号查医嘱如果查询秒出印象分会高不少。第四步是“备份恢复演练”。导出完整库再导入到另一备库确认两边行数一致mysqldump -u root -p hospital_db hospital_db_backup.sql mysql -u root -p -e CREATE DATABASE hospital_db_test DEFAULT CHARACTER SET utf8mb4; mysql -u root -p hospital_db_test hospital_db_backup.sql四步走完这份资源里的代码你才算真正消化完。从那以后我每次拿到课设包都强制走一遍这套流程已经少踩一半坑希望帮到你。如果你手头正缺一套能直接跑的病房管理课设参考这套资源值得下载后按上面的顺序重新验证一遍。本文还有配套的精品资源点击获取
返回列表