ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

医院门诊管理系统数据库设计课程设计:从需求分析到SQL Server与Oracle双平台建库

医院门诊管理系统数据库设计课程设计:从需求分析到SQL Server与Oracle双平台建库 简介这份资源是面向软件工程、计算机相关专业学生的数据库课程设计参考文档聚焦医院门诊管理系统的数据库设计适合正在完成课程设计或需要撰写数据库设计论文的学习者使用。文档采用结构化分析与设计方法围绕需求分析、数据流程图、数据字典、概念设计、逻辑设计、物理设计及数据库实施与测试等环节展开完整呈现了从E-R图建立到关系模式规范化、SQL Server 2008数据库落地的设计思路并覆盖登记挂号、诊断治疗、收费挂号等门诊基本业务流程。资源包共1个doc文件大小约731KB内容为完整的课程设计论文含摘要、目录与各章节正文便于直接参考写作框架与设计流程。目前已有2832人学习下载适合需要借鉴数据库设计方法、梳理课程设计结构或查漏补缺的读者。1. 从一份 2014 年的课程设计文档说起它到底能帮你省下多少事如果你正在为数据库课程设计发愁或者带学生做门诊管理系统这类经典题目这份《医院门诊管理系统数据库设计课程设计.doc》大概率能直接当模板用。它不是那种只给几张 E-R 图就交差的半成品而是一份从需求分析一路写到 SQL Server 2008 和 Oracle 双平台建库脚本的完整论文正文里连数据字典的 35 个数据项、8 个数据结构、11 条数据流都列全了。换句话说你拿到手的不只是一份文档而是一套可以直接照着敲、照着改、照着答辩的数据库设计全流程素材。适合软件工程、计算机专业正在做课程设计的学生也适合需要快速搭一个门诊业务原型库的开发者。下面我按自己拆文档的习惯把这份资源里真正能落地的部分拆开讲。2. 需求分析怎么落到数据字典35 个数据项不是凑数的2.1 数据流程图的三层拆法文档里把数据流程图分成了顶层、第一层、第二层第二层又按挂号收费、诊断、取药三条业务线各画一张。这个拆法不是随便分的它对应的是门诊系统里三条独立的数据流病人从挂号到缴费是一条线从就诊到确诊是一条线从开方到取药是第三条线。三条线在收费单和处方单这两个实体上交汇所以后面 E-R 图里收费单和处方单都是多对多关系的核心节点。我一般带人做需求分析时会强调一点数据流程图不是画给老师看的是画给自己看的。你后面建表时每加一个外键都应该能在数据流程图里找到对应的数据流。比如文档里 F3 缴费这条流来源是病人、去向是收费处理组成是收费信息高峰流量标了每日 5000 次。这个数字直接决定了后面收费单表要不要做分区、索引怎么建。2.2 数据字典五件套的填写逻辑数据字典包含数据项、数据结构、数据流、处理逻辑、数据存储五个部分文档里分别给了 35、8、11、6、8 条记录。这个数量不是硬凑的我核对过每个数据项都能在后面的关系模式里找到对应的字段。数据项表里有个细节值得注意病人编号 Pno 和医生号 Dno 都定义为 varchar(20)而不是 int。这是课程设计里常见的做法因为学号、工号这类编号往往带前缀字母用字符串更灵活。但代价是索引体积会比整型大如果真上生产环境我一般会建议改成 char(10) 定长或者用自增整型做主键、业务编号做唯一索引。数据结构表里 DS-8 诊断结果由 Dno、Pno、Iname、Pr_no 四个属性组成其中 Dno 和 Pno 既是主键又是外键。这种复合主键的设计在诊断结果表里是合理的因为一个医生对同一个病人同一时间只应该有一条诊断记录。但文档后面建表时 Diagnose 表的主键写的是 primary key(Dno,Pno)而 Pr_no 只是外键这意味着一个诊断结果只能对应一张处方。如果实际业务里一次诊断开多张处方这个设计就得改。2.3 处理逻辑与数据存储的对应关系处理逻辑表里 P1.1 挂号、P1.2 收费、P1.3 分配医师、P2.1 诊断、P2.2 确诊、P3.1 取药一共六个处理。数据存储表里 S1 到 S8 分别对应挂号记录、收费记录、值班医生记录、诊断记录、药物记录、收费款项、收费标准、处方。你仔细看会发现每个处理逻辑至少对应一个数据存储的读写。比如 P1.1 挂号处理输入是病人信息输出是挂号单相关的数据存储是 S1 挂号记录。这意味着挂号这个动作至少要写一张挂号单表同时可能还要读病人表确认病人是否存在。文档后面建的 Register 表正好对应 S1字段有 Rno、Rway、Rdate、Pno、Bno和数据结构 DS-5 完全一致。提示做课程设计时数据字典里的每一条数据流都应该能在数据流程图里找到箭头每一个数据存储都应该能在后面的建表脚本里找到对应的 create table。如果对不上答辩时老师一问就露馅。3. 从 E-R 图到三范式关系模式八个表的转换逻辑3.1 分 E-R 图到全局 E-R 图的合并规则文档里先按第二层数据流程图建了三张分 E-R 图然后消除冲突和冗余合成全局 E-R 图。这个顺序很重要很多同学直接画全局图结果实体和联系漏了一堆。分图建的时候挂号收费这条线会产生病人、医生、挂号单、收费单四个实体诊断这条线会产生诊断结果、处方两个实体取药这条线会产生药品实体。合并的时候病人和医生在两个分图里都出现了需要统一属性定义这就是消除冲突。全局 E-R 图里病人和医生是多对一病人和挂号单是一对一科室和医生是一对多医生和诊断结果是一对多诊断结果和处方单是一对一处方单、收费单、药品之间是三元关系。这些基数关系直接决定了后面关系模式转换时外键往哪边放。3.2 关系模式转换的三条规则文档里写得很清楚1:1 联系可以独立成表也可以合并到任意一端1:n 联系可以独立成表也可以合并到 n 端m:n 联系必须独立成表。这份设计里病人和医生是 n:1所以把医生号 Dno 放到病人表里做外键科室和医生是 1:n把科室号 Dp_no 放到医生表里做外键诊断结果和处方单是 1:1把处方号 Pr_no 放到诊断结果表里。处方单、收费单、药品之间的三元关系比较特殊文档把它转换成了独立的处方表字段有 Pr_no、Pr_date、Mno、Bno。这里 Mno 和 Bno 都是外键分别指向药品表和收费单表。这种设计的好处是处方表本身不冗余坏处是查一次处方明细要 join 三张表。3.3 三范式检查与用户子模式文档声称所有关系模式都符合三范式。我逐表核对过病人表里 Pno 是主键Pname、Psex、Page 都直接依赖 PnoDno 作为外键也直接依赖 Pno没有传递依赖医生表里 Dno 是主键Dname、Dtitle、Dtel 直接依赖 DnoDp_no 作为外键也直接依赖 Dno药品表里 Mno 是主键Mname、Mprice、Mquantity 都直接依赖 Mno。确实都满足三范式。用户子模式建了六个视图收费细则视图、病人-药品视图、诊断结果视图、医生病人视图、科室医生视图、病人挂号视图。这些视图的作用是简化查询和做权限隔离。比如收费细则视图 BillDetail 把诊断、处方、收费单、挂号四张表 join 在一起直接查这个视图就能拿到病人号、收费单号、日期、金额、收费方式不用每次写四表连接。-- 收费细则视图把诊断、处方、收费单、挂号四张表串起来 create view BillDetail as select distinct Diagnose.pno, Bill.Bno, Bdate, Bmoney, Bway from Prescription, Bill, Diagnose, Register where Register.pno Diagnose.pno and (Diagnose.Pr_no Prescription.Pr_no and Prescription.bno Bill.bno or Register.bno Bill.bno)这段代码里用了 distinct 去重因为一个病人可能既有挂号收费又有药品收费join 之后会出现重复行。where 条件里用了 or把挂号收费和药品收费两条路径都覆盖了。参数上BillDetail 视图没有输入参数直接 select 就能用。如果你要按病人号过滤外面再套一层 where pno xxx 就行。注意视图里的 or 条件会导致查询优化器很难走索引数据量大的时候性能会明显下降。课程设计数据量小无所谓真上生产建议拆成两个视图或者用 union all。4. SQL Server 2008 与 Oracle 双平台建库脚本差异与实施顺序4.1 基本表与约束的建表顺序文档里 SQL Server 2008 部分先建了 Department、Doctor、Patient、Medicine、Bill、Prescription、Diagnose、Register 八张表。这个顺序不能乱因为外键引用的表必须先存在。Department 最先建因为 Doctor 引用它Doctor 第二因为 Patient 引用它Patient 和 Medicine 建完之后才能建 Prescription 和 DiagnoseBill 建完之后才能建 Register。-- 科室表最先建被医生表引用 create table Department ( Dp_no varchar(20) primary key, Dp_name varchar(20) not null, Dp_tel varchar(20) ); -- 医生表引用科室表 create table Doctor ( Dno varchar(20) primary key, Dname varchar(20) not null, Dtitle varchar(20), Dp_no varchar(20) references Department(Dp_no), Dtel varchar(20) ); -- 病人表引用医生表年龄加了 check 约束 create table Patient ( Pno varchar(20) primary key, Pname varchar(20), Psex varchar(20), Page int check(Page 0 and Page 150), Dno varchar(20) references Doctor(Dno) );Patient 表的 Page 字段加了 check 约束限制在 0 到 150 之间。这个范围是合理的但实际业务里年龄一般不会超过 120150 留了余量。Dno 作为外键引用 Doctor 表意味着一个病人必须有一个主治医生。如果业务允许病人先挂号后分配医生这个外键就得允许 null或者把 Dno 从 Patient 表挪到 Register 表。4.2 存储过程与触发器的业务封装文档里写了九个存储过程覆盖了新增病人挂号、新增诊断结果、插入药品、修改科室电话、修改药品库存、查询科室医生人数、查询病人主治医生、查询医生主治病人、查询感冒病人姓名。这些存储过程把多表插入和查询封装成了一个调用应用程序只需要传参数就行。-- 新增病人挂号同时写病人表、收费单表、挂号单表 create proc addpatient Rno varchar(20), Rway varchar(20), Pno varchar(20), Bno varchar(20), Pname varchar(20), Psex varchar(20), Page int, Dno varchar(20), Bmoney float as insert into Patient values(Pno, Pname, Psex, Page, Dno) insert into Bill values(Bno, GETDATE(), Bmoney, 挂号收费) insert into Register values(Rno, Rway, GETDATE(), Pno, Bno)这个存储过程没有用事务包裹三条 insert 如果中间一条失败前面的不会回滚。课程设计里数据量小一般不会出问题但真上生产必须加 begin transaction 和 commit/rollback。参数上Bmoney 是挂号费Rway 是挂号方式Rno 是挂号单号这三个由应用程序传入。GETDATE() 是 SQL Server 的内置函数Oracle 里对应 sysdate。4.3 Oracle 实施的差异点文档里 Oracle 部分和 SQL Server 部分结构一样但语法有差异。比如 SQL Server 的 varchar 在 Oracle 里一般用 varchar2date 类型在 Oracle 里包含时间部分float 在 Oracle 里可以用 number。存储过程里 SQL Server 用 开头定义参数Oracle 用 p_ 前缀或者直接写参数名。如果你要在 MySQL 里复现这份设计常见做法是把 varchar(20) 改成 varchar(20) 或 char(20)把 GETDATE() 改成 now()把存储过程的 create proc 改成 create procedure参数用 in 关键字。索引部分 create unique index 在 MySQL 里语法基本一致但 asc 可以省略。提示跨数据库平台移植时最容易被忽略的是日期类型和自增主键。SQL Server 的 identity、Oracle 的 sequence、MySQL 的 auto_increment 写法完全不同课程设计里用业务编号做主键反而省事。5. 避坑与排查课程设计里最容易翻车的五个地方5.1 外键引用顺序错误导致建表失败现象执行 create table 时提示“引用了无效的表”或“外键约束失败”。原因被引用的表还没建或者引用的字段不是主键或唯一键。解决按 Department → Doctor → Patient → Medicine → Bill → Prescription → Diagnose → Register 的顺序建表确保每个 references 后面的字段在目标表里是 primary key 或 unique。5.2 存储过程参数类型不匹配现象调用存储过程时提示“参数数据类型不兼容”或“隐式转换失败”。原因传入的参数类型和定义的类型不一致比如定义的是 varchar(20)传入了 int 或者超长字符串。解决调用前用 cast 或 convert 显式转换或者把存储过程参数类型放宽到 varchar(50)。我一般会在存储过程开头加一层参数校验长度超限直接 return。5.3 视图 join 导致数据重复现象查 BillDetail 视图时同一个收费单号出现多次。原因四张表 join 时一个病人可能有多条挂号记录和多条处方记录笛卡尔积导致重复。解决用 distinct 去重或者把视图拆成挂号收费和药品收费两个独立视图。文档里用了 distinct但 distinct 对大数据量性能影响很大课程设计数据少可以接受。5.4 年龄 check 约束与业务冲突现象插入病人记录时提示“违反了 check 约束”。原因Page 字段限制在 0 到 150但实际录入时可能填了负数或者超过 150。解决应用程序层做校验或者把 check 范围放宽到 0 到 200。如果业务允许年龄为空还要允许 null因为 check 约束对 null 是放行的。5.5 Oracle 与 SQL Server 日期格式差异现象在 Oracle 里执行 GETDATE() 报错。原因GETDATE() 是 SQL Server 函数Oracle 用 sysdate 或 current_date。解决写跨平台脚本时日期统一用参数传入不要在 SQL 里硬编码函数。如果必须用SQL Server 用 GETDATE()Oracle 用 sysdateMySQL 用 now()。6. 进阶用法把这份设计改成可运行的 MySQL 版本6.1 建库与建表脚本改写这份文档的 SQL Server 脚本要跑在 MySQL 里需要改几个地方varchar 保持不变date 改成 datefloat 改成 decimal(10,2) 更精确GETDATE() 改成 now()存储过程的 create proc 改成 create procedure 并调整参数语法。下面是我改好的 MySQL 版核心建表脚本。-- MySQL 版建库 create database Hospital default charset utf8mb4; use Hospital; -- 科室表 create table Department ( Dp_no varchar(20) primary key, Dp_name varchar(20) not null, Dp_tel varchar(20) ) engineInnoDB; -- 医生表 create table Doctor ( Dno varchar(20) primary key, Dname varchar(20) not null, Dtitle varchar(20), Dp_no varchar(20), Dtel varchar(20), constraint fk_doctor_dept foreign key (Dp_no) references Department(Dp_no) ) engineInnoDB; -- 病人表 create table Patient ( Pno varchar(20) primary key, Pname varchar(20), Psex varchar(20), Page int, Dno varchar(20), constraint chk_page check (Page 0 and Page 150), constraint fk_patient_doctor foreign key (Dno) references Doctor(Dno) ) engineInnoDB;MySQL 里 check 约束在 8.0.16 之前是不生效的8.0.16 之后才真正执行。如果你用的是 5.7年龄校验得放到应用程序层做。外键约束我加了命名方便后面排查和删除。engineInnoDB 是必须的因为 MyISAM 不支持外键。6.2 存储过程改写与测试MySQL 的存储过程语法和 SQL Server 差异较大参数不用 开头变量声明用 declare语句块用 begin...end 包裹。下面是 addpatient 的 MySQL 版。delimiter // create procedure addpatient( in p_Rno varchar(20), in p_Rway varchar(20), in p_Pno varchar(20), in p_Bno varchar(20), in p_Pname varchar(20), in p_Psex varchar(20), in p_Page int, in p_Dno varchar(20), in p_Bmoney decimal(10,2) ) begin declare exit handler for sqlexception begin rollback; end; start transaction; insert into Patient values(p_Pno, p_Pname, p_Psex, p_Page, p_Dno); insert into Bill values(p_Bno, now(), p_Bmoney, 挂号收费); insert into Register values(p_Rno, p_Rway, now(), p_Pno, p_Bno); commit; end // delimiter ;这个版本加了事务和异常处理比原文档的 SQL Server 版更健壮。exit handler 捕获 sqlexception 后回滚保证三条 insert 要么全成功要么全失败。调用时用 call addpatient(R001,现场,P001,B001,张三,男,30,D001,10.00); 就能一次性完成挂号、收费、病人登记三个动作。6.3 验证方法与数据入库测试改完之后怎么验证我一般分三步走。第一步执行 show tables; 确认八张表都建好了。第二步执行 show create table Patient; 确认外键和 check 约束都在。第三步插入测试数据先插科室再插医生再插病人最后调存储过程走一遍挂号流程。-- 测试数据入库 insert into Department values(DP01,内科,010-12345678); insert into Doctor values(D001,李医生,主任医师,DP01,13800000000); insert into Medicine values(M001,阿莫西林,25.50,100); -- 调用存储过程完成挂号 call addpatient(R001,现场,P001,B001,张三,男,30,D001,10.00); -- 验证结果 select * from Register where Rno R001; select * from Bill where Bno B001; select * from Patient where Pno P001;如果三条 select 都能查到数据说明存储过程执行成功。如果报错先看错误信息里的约束名fk_patient_doctor 说明医生号不存在chk_page 说明年龄超范围。我习惯在测试完之后把测试数据删掉避免污染正式数据。6.4 索引优化与查询验证文档里建了三个索引idx_bno 按收费单号升序、unique_pname 病人姓名唯一、unique_mname 药品名称唯一。MySQL 里 create unique index 语法一致但 asc 可以省略。我一般还会给 Register 表的 Pno 和 Bno 加索引因为按病人查挂号记录和按收费单查挂号记录是高频操作。-- 补充索引 create index idx_register_pno on Register(Pno); create index idx_register_bno on Register(Bno); create index idx_diagnose_pno on Diagnose(Pno); -- 验证索引是否生效 explain select * from Register where Pno P001;explain 的输出里 key 列显示 idx_register_pno 就说明索引生效了。如果 type 是 ALL说明走了全表扫描数据量大的时候需要检查索引是否建对。从那以后我每次拿到这类课程设计文档都会先跑一遍建表脚本再插三条测试数据最后调一遍核心存储过程。这套流程走下来文档里藏着的语法错误和逻辑漏洞基本都能暴露出来。希望这份拆解能帮到你少走点我当年踩过的弯路。本文还有配套的精品资源点击获取
返回列表