简介:医院信息系统(HIS)数据库设计是医疗信息化建设的核心环节,涉及门诊、住院、药房、财务等多部门、多角色的复杂数据关系。此文档面向数据库设计人员、医疗信息化从业者及高校相关专业学生,以医院真实业务流程为背景,梳理各部门的功能划分与协作方式,并重点讲解需求分析、ER图建模、关系表设计、外键关联、索引优化等内容,同时对数据完整性、一致性、安全性、系统性能、可扩展性及法规遵从性等关键设计原则给出说明。资源包内为1个docx文档,总大小398KB,内容以文字方案和图表设计为主,便于查阅和二次修改。目前已有173人学习下载。文档结合具体业务场景,详述了病人挂号、缴费、取药、检查、住院、手术等环节的数据流转与存储需求,并给出实体划分、约束设置、索引设计等实操建议,可作为医院管理系统数据库课程设计、毕业设计或项目实现的参考模板。
1. 医院管理系统数据库设计:先想清楚这 20 张表是给谁用的
医院管理系统数据库设计,听起来是张表结构清单,实际上一开始就要回答一个总被跳过的问题:这些表到底要撑起谁的业务?课程作业里,用户信息表、挂号表、药品表画出来能跑增删改查就算交差;真实 HIS 项目里,患者、挂号、收费、药库、结算必须串成一条丢不了数据、重复计费就会出事的闭环。这中间差的不是表数量,是对状态、金额、并发这三个词的理解。这篇笔记按后者来写,先讲表为什么这么拆,再给建表脚本、字段参数和踩坑记录,最后是一份可以直接拿去评审的检查清单。适合正在做课设、毕业设计,或者要接手医院类系统数据库设计的人照着复现。
2. 患者主索引与挂号模型:这 5 张表决定系统能不能撑住门诊高峰
门诊系统最大的压力点不在并发,而在“同一个患者被抓出来三份档案”。患者挂号、医生写病历、药房发药、收费结算,全都要先定位到人。如果患者数据乱,后面所有表的关联都跟着出问题。这一章先把用户、患者、挂号、就诊这几张地基表讲透。
2.1 用户信息表与患者表为什么要拆开:两种完全不同的实体
很多实训平台把医院管理系统数据库设计拆成“第 1 关:用户信息表”起步,这一步题目常常是让建一张包含用户名、密码、角色、科室的 user 表。没问题,但注意:这里说的“用户”指的是能登录系统的人——医生、护士、药师、管理员、收费员。而“患者”是不登录系统的,他只是被服务的人,两者的字段差异大到没法塞进一张表。
医生有工号、职称、科室、排班;患者有证件号、过敏史、医保类型、紧急联系人。如果你把患者也塞进 user 表,就得给大部分行留空字段,更严重的是权限模型会乱:一个收费员理论上不该有权限开处方,如果都在一张表里,角色判断的边界就会被模糊掉。所以我的习惯是分两张表:sys_user 管账号和权限,patient 管就诊人档案,中间通过创建关系、就诊关系关联,而不是混在一起。
还要说一个和博客系统用户表的差别。博客系统的 user 表加个 role 字段就够了,因为博主和读者的字段几乎一样;医院不行,患者档案里的出生日期要精确到天、证件号要校验、过敏史是结构化字段,这些在账号表里都属于彻底无关的冗余。拆表不是过度设计,是两类数据生命周期不同:账号可能因为离职被禁用,但患者的就诊档案要保留几十年。
2.2 患者主索引:证件号有重号、有换号,不能拿来做外键
患者表建好后,紧接着就是主键选什么。最常见的新手做法是直接拿身份证号当主键,理由是身份证唯一且稳定。但真实医院场景它撑不住,原因有三个:第一,身份证号要脱敏存储和处理,直接当主键等于明文管理;第二,未上户口的婴儿、外籍人员没有身份证号;第三,同一患者可能用身份证挂一次号、用医保卡又挂一次,如果两套卡号都关联到不同 patient_id,这个人在系统里就是两个人。
所以业界用患者主索引(EMPI)来解决。简单说就是系统维护一个全局唯一的 patient_id,无论患者用身份证、医保卡还是自费卡建档,都通过证件类型加证件号、姓名加出生日期等规则去匹配已有的 patient_id,匹配上了就复用,匹配不上才新建。所有业务表只引用 patient_id,证件号只是普通字段加唯一索引,但允许为空。
设计文档里这张表至少要包含:patient_id(主键)、id_card(证件号)、id_type(证件类型)、name、gender、birth_date、medical_no(医保卡号)、phone、address、allergy(过敏史)。其中 gender 建议用 tinyint 字典,0 未知 1 男 2 女,别直接用字符串,后面写接口和统计都省事。birth_date 用 DATE,不要用 VARCHAR,不然年龄计算和按出生年统计会变成一场灾难。
2.3 挂号与就诊:退号不是删记录,是状态流转
挂号表 registration 是门诊的第一个流量入口。它记录的是“某位患者在某一天挂了某个科室某个医生的某个号别”。核心字段有 reg_id、patient_id、visit_date、time_slot(时段:上午/下午/晚间)、dept_id、doctor_id、reg_type(普通/专家/急诊)、status(待就诊/已就诊/已退号/爽约)、fee。
这里有个设计原则:退号只能做状态流转,不能物理 DELETE。原因很实际:退号率是门诊管理的考核指标,财务要对账,医生排班要统计爽约,一旦删了记录,报表全是窟窿。我见过把退号做成 DELETE 的早期版本,月底统计退号率时永远少算,最后只能从操作日志里手工捞数据补。所以状态字段从建表第一天就定死:0 待就诊、1 已就诊、2 已退号、3 爽约,谁改的、什么时候改的,另行记录在操作日志表里。
就诊表 visit 则挂更高一层的概念:一次“就诊”是指患者实际接受诊疗的过程。理论上一次挂号对应一次就诊,但真实情况里:专家让患者先去做检查,检查完回来复诊,有的系统会让同一次挂号内产生多条 visit 记录;住院患者根本没有挂号,直接办入院就产生 visit。所以 visit 表建议独立于 registration,用 reg_id 可空外键关联。visit 表至少要有 visit_id、patient_id、reg_id(可空)、visit_type(门诊/急诊/住院/体检)、dept_id、doctor_id、visit_start_time、visit_end_time、status。这样一来,处方、检验、检查、费用明细全部挂在 visit_id 下,而不是挂在挂号下,数据链路就清晰了。
2.4 五张核心表的字段清单参考
下面这张表可以直接抄进你的《医院管理系统数据库设计.docx》文档里。类型以 MySQL 8 为例,主键统一用 BIGINT,ID 生成策略见第 4 章。
| 表名 | 核心字段 | 类型 | 约束与说明 |
|---|---|---|---|
| sys_user | user_id, username, password_hash, real_name, role, dept_id, phone, status | BIGINT, VARCHAR(50), VARCHAR(100), TINYINT, BIGINT | username 唯一;role 1 管理员 2 医生 3 药师 4 收费员 5 护士 |
| patient | patient_id, id_card, id_type, name, gender, birth_date, medical_no, phone, allergy | BIGINT, VARCHAR(18), TINYINT, VARCHAR(50), TINYINT, DATE | id_card 加唯一索引但允许为空;id_type 1 身份证 2 医保卡 3 护照 4 港澳台 |
| registration | reg_id, patient_id, dept_id, doctor_id, visit_date, time_slot, reg_type, status, fee | BIGINT | 联合索引 (dept_id, visit_date, time_slot) 查剩余号源;status 用 TINYINT 字典 |
| visit | visit_id, patient_id, reg_id, visit_type, dept_id, doctor_id, visit_start_time, visit_end_time, status | BIGINT | reg_id 可空,住院场景不经过挂号表 |
| op_log | log_id, operator_id, op_type, target_table, target_id, before_json, after_json, created_at | BIGINT | 记录关键表的变更前/后内容,做审计后悔药 |
注意 id_card 加唯一索引但不做主键,这个细节承上启下:唯一索引保证不重复建档,主键用语义无关的 ID 保证换号、改号不影响业务表关联。
3. 费用与药库闭环:收费项目、处方明细与库存流水怎么串成一条链
门诊高峰时一个药师一小时发两百盒药,收费员一上午对一百张处方。如果费用和库存的数据模型是断的,轻则对账不平,重则药房发错药、财务查不出钱去哪了。这一章把“医生开单—收费—药房发药—月底盘点”整个闭环串起来。
3.1 收费项目字典:价格是字典的字段,不是业务表的字段
医生开的处方上写的是“阿莫西林胶囊 0.5g×24 粒”,收费时要知道它的单价,药房要知道它的规格。如果每次开单都把价格和规格复制一份到处方明细表里,短期看着方便,一旦医保调价或者药品换包装,历史账单就全部失真——结算单上显示的是旧价格,财务新老对不上。
所以要有 charge_item 收费项目字典表。凡是能收钱的东西都算“项目”:药品、检查、检验、治疗、材料、挂号费,统一进这一张表。字段至少包含 item_code(项目编码)、item_name(项目名称)、category(项目分类)、specification(规格)、unit(单位)、unit_price(单价)、pinyin_code(拼音码,用于医生开单时简拼检索)、enabled(是否在架)、effective_date。
effective_date 是容易被忽略但必须有的字段。调价不是把 unit_price 改了就行,而是要支持“调价前开的单按老价结算,调价后按新价结算”。常见做法是同一 item_code 的多条记录用 effective_date 区分版本,计价引擎取就诊时间对应的版本。顺便说一句:苍穹外卖那些课设项目的菜品表之所以不用管价格版本,是因为外卖菜品调价不涉及医保对账和退款追溯,而医院收费每一笔都可能在几天后退费重算,没有版本寸步难行。
3.2 处方明细与费用账单:一单一明细,金额计算放在收费引擎
处方主表 prescription 挂在 visit_id 下,记录一次就诊开了哪些处方。字段有 prescription_id、visit_id、prescription_type(西药/中成药/检验/检查/治疗)、doctor_id、status(已开立/已收费/已发药/已作废)、created_at。处方明细表 prescription_detail 则一行一条项目,字段有 detail_id、prescription_id、item_code、quantity、dosage、frequency、days、unit_price。
设计这笔金额时记住一条原则:明细表里可以冗余“收费时的单价”,但不要把“应收金额”和“实收金额”混进去。因为医院计费有三种来源:自费、医保、公费,医保还要分甲类乙类自付比例。最终应收金额由收费引擎根据项目类别、医保类型、折扣规则计算得出,结果写进费用表 fee。fee 表的最小字段是 fee_id、visit_id、patient_id、fee_type(自费/医保/公费)、total_amount、discount_amount、actual_amount、status(待结算/已结算/已退费)、settle_time。
为什么不在明细表里直接写死应收?因为一张处方可能部分自费部分医保,还可能在第 20 天来退费,退费只退医保范围内的那一小项。如果每行明细都有自己的一套金额逻辑,退费功能就是一场噩梦。反过来,明细只保留“数量 × 收费单价”的原始数据,金额计算全部集中在收费引擎,退费时重算就行。
3.3 药库两张表:库存表和流水表,先写流水再改库存
药库的模型比一般进销存多一个约束:同一药品不同批号、不同有效期要分开管理,因为医院有近效期预警和批号追踪的硬要求。库存表 drug_stock 的主键建议是复合的 (drug_id, batch_no),字段有 drug_id、batch_no、expire_date、quantity、unit,quantity 只表示当前结存。
另一张表 stock_flow 记录每一次数量变化,字段有 flow_id、drug_id、batch_no、change_type(入库/出库/报损/退库/盘点调整/期初)、change_quantity(正数入负数出)、before_quantity、after_quantity、operator_id、created_at、ref_id(关联的处方或单据号)。
出库发药时的正确事务顺序是:先写 stock_flow 流水,再 UPDATE drug_stock 扣减结存,两步在同一个数据库事务里。流水表的 before/after 快照就是日后盘点的后悔药——月底盘亏一盒药,翻流水能倒查到是哪一笔处方、哪个窗口、哪个操作者。如果只改库存不写流水,盘亏时数据表里干干净净,你连从哪查起都不知道,那种黑匣子状态做一次就知道有多难受。
3.4 费用与库存相关表的字段清单参考
费用与库存一共涉及六张表:charge_item、prescription、prescription_detail、fee、drug_stock、stock_flow。核心字段见下表。
| 表名 | 核心字段 | 类型 | 约束与说明 |
|---|---|---|---|
| charge_item | item_code, item_name, category, specification, unit, unit_price, pinyin_code, effective_date, enabled | VARCHAR / DECIMAL(10,2) | item_code 加历史版本,唯一索引 (item_code, effective_date) |
| prescription | prescription_id, visit_id, prescription_type, doctor_id, status | BIGINT | 联合索引 (visit_id, status) 支撑“某次就诊有哪些未收费处方” |
| prescription_detail | detail_id, prescription_id, item_code, quantity, unit_price, dosage, frequency, days | BIGINT / DECIMAL(10,2) | 不存应收金额,只存原始数量和开单单价 |
| fee | fee_id, visit_id, patient_id, fee_type, total_amount, discount_amount, actual_amount, status, settle_time | BIGINT / DECIMAL(10,2) | 金额一律 DECIMAL,不允许 FLOAT |
| drug_stock | drug_id, batch_no, expire_date, quantity, unit | DECIMAL(10,3) | 复合主键 (drug_id, batch_no),数量允许三位小数 |
| stock_flow | flow_id, drug_id, batch_no, change_type, change_quantity, before_quantity, after_quantity, operator_id, ref_id | BIGINT / DECIMAL(10,3) | before/after 快照必须记录,用于审计 |
4. 把 ER 图落成可执行的建表脚本:字符集、主键策略与三条索引规则
设计文档画到 ER 图只是完成一半,另一半是把图变成能跑、能扛并发、能导入历史数据的 DDL。这一章从建库语句开始,把字符集、主键、外键、索引四个从文档到落地最容易返工的地方讲透。
4.1 建库之前的三个决定:字符集、引擎与 ID 生成
第一个决定是字符集。MySQL 8 默认就是 utf8mb4,它能存四字节表情和生僻字,而医院患者姓名里的生僻字比你想的多得多,“玥”“彧”这些都算常见了。排序规则用 utf8mb4_general_ci 还是 utf8mb4_unicode_ci,对医院系统这种重查询轻排序的场景差别不大,统一用一种就行,但整库至少不要混用两种排序规则,否则 JOIN 时索引失效。
第二个决定是引擎,生产必须用 InnoDB。理由不是“默认”,而是 InnoDB 有行锁和事务。第 3 章说到的“先写流水再扣库存”依赖事务,MyISAM 连事务都没有,真做并发发药会直接翻车。
第三个决定是主键 ID 怎么生成。单机课设用自增 BIGINT 最省事,但如果你做的是多院区或者要合并历史库,自增主键会撞车。医院系统我的习惯是:业务主键用雪花 ID(BIGINT 存储),审计类流水表用自增。雪花 ID 本身带时间信息,拆库、合并、分表都方便。设计文档里主键字段统一写成 BIGINT NOT NULL COMMENT '雪花ID',比让读者自己选省一个坑。
4.2 从设计文档到 DDL:患者、挂号、就诊三表的最小建表脚本
下面这段 SQL 是前面两张表的最小落地版本,可以直接在 MySQL 8 里执行。为了突出“设计文档怎么翻译成 DDL”,外键我先用普通索引代替,原因在本章最后说明。
-- 患者主索引表,档案与账号分离 CREATE TABLE patient ( patient_id BIGINT NOT NULL COMMENT '雪花ID', id_card VARCHAR(18) NULL COMMENT '证件号,允许为空', id_type TINYINT NOT NULL DEFAULT 1 COMMENT '证件类型:1身份证 2医保卡 3护照', name VARCHAR(50) NOT NULL COMMENT '姓名', gender TINYINT NOT NULL DEFAULT 0 COMMENT '0未知 1男 2女', birth_date DATE NOT NULL COMMENT '出生日期', medical_no VARCHAR(32) NULL COMMENT '医保卡号', phone VARCHAR(20) NULL COMMENT '联系电话', allergy VARCHAR(255) NULL COMMENT '过敏史,自由文本', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (patient_id), UNIQUE KEY uk_patient_idcard (id_card), KEY idx_patient_name (name) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='患者档案表'; -- 挂号表,退号通过状态流转 CREATE TABLE registration ( reg_id BIGINT NOT NULL COMMENT '挂号流水ID', patient_id BIGINT NOT NULL COMMENT '患者ID', dept_id BIGINT NOT NULL COMMENT '科室ID', doctor_id BIGINT NOT NULL COMMENT '医生ID', visit_date DATE NOT NULL COMMENT '就诊日期', time_slot TINYINT NOT NULL COMMENT '1上午 2下午 3晚间', reg_type TINYINT NOT NULL DEFAULT 1 COMMENT '1普通 2专家 3急诊', status TINYINT NOT NULL DEFAULT 0 COMMENT '0待就诊 1已就诊 2已退号 3爽约', fee DECIMAL(8,2) NOT NULL DEFAULT 0 COMMENT '挂号费', created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (reg_id), KEY idx_reg_dept_date (dept_id, visit_date, time_slot), KEY idx_reg_patient_date (patient_id, visit_date) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='门诊挂号表';这段脚本里有两个参数值得解释。dept_id 和 doctor_id 没有建外键,只建了普通索引,一方面是因为它们关联的是科室表和医生表,而医生表属于 sys_user 体系;另一方面是医院数据库经常要拆库,物理外键在迁移时是最先被删的东西。第二个关键是 idx_reg_dept_date 联合索引,它正好给“某天某科室还剩多少号”这个最热门的查询用,叶子节点已经按科室和时间排好序,MySQL 走索引就能数出号源,不用回表。
如果你做课设,可以把外键补上,让 ER 图跟库一致;但生产环境我强烈建议外键只存在于设计文档的“逻辑外键”说明里,物理外键删掉,只保留索引。逻辑外键保证数据一致性靠的是应用层事务,物理外键在导入历史数据时是最大的性能杀手,第 5 章有对应踩坑记录。
提示:外键决策按数据量来。10 万行以内的课设数据,物理外键带来的便利大于开销;百万级的历史导入,物理外键是第一个被拆掉的。
4.3 索引怎么加:三个高频查询决定索引组合
医院门诊库的高频查询不超过十个,索引设计别贪多,每张表三到五个就够。我一般会按查询条件反推:
- 挂号剩余号源:WHERE dept_id = ? AND visit_date = ? AND time_slot = ?,对应 idx_reg_dept_date。
- 患者历史挂号:WHERE patient_id = ? ORDER BY visit_date DESC,patient_id 单列索引就行,或者联合 (patient_id, visit_date) 让覆盖索引直接返回。
- 医生某天排班:WHERE doctor_id = ? AND visit_date >= ? AND visit_date <= ?,建议 (doctor_id, visit_date)。
一个高频误用是在 status 字段上建单列索引。status 的取值就 0 到 3,区分度极低,优化器大概率全表扫也不会走索引。真要按状态过滤,把它放到联合索引的末尾,比如 (dept_id, visit_date, status),还能省一次回表。索引字段的选择直接决定查询是毫秒级还是秒级,这个在数据量小的课设里看不出来,导入几十万条历史数据后立见分晓。
5. 医院管理系统数据库设计避坑:五个让我返工过的实际问题
数据库设计的问题,往往要上线后一个月才暴露,而修复成本是设计阶段的几十倍。下面五条是医院类系统里很常见的坑,按“现象—原因—解决”写清楚,能帮你少走一半弯路。
5.1 患者手机号被当主键:一个人给全家人挂号就炸了
现象:患者表用手机号 mobile 做主键,设计评审时看着挺合理——手机号唯一嘛。上线后第一个月没事,第二个月有位用户用自己手机号给家里老人挂号,系统提示手机号已存在,建档失败。而且老人后来换手机号,所有关联记录都跟着改。
原因:把业务上的“唯一值”当成了主键。手机号是业务属性,可能换、可能被多个就诊人共用,主键却要求语义无关且永久不变。
解决:patient_id 独立成雪花 ID 主键,mobile 只是普通字段,允许为空,按业务需要建唯一索引。如果确实要支持“一个手机号绑多个就诊人”,手机号就连唯一索引都不要建,放到一个单独的账号绑定表里。
5.2 金额字段用 FLOAT:对账差两分钱查了一下午
现象:收费模块每天对账都差几分钱,打印出来的小票金额出现 9.999999 这种值。
原因:FLOAT 是二进制浮点,无法精确表示 0.1;乘法加法累积出误差。
解决:金额统一 DECIMAL(10,2),数量和剂量用 DECIMAL(10,3),因为有的药品按克开。这个坑只要在设计文档里出现一个 FLOAT 金额,后面所有表都要跟着改一遍,所以我的习惯是文档里直接约定:凡涉及钱和数量的列,一律 DECIMAL,并在字段说明里写死精度。
5.3 药品库存直接 UPDATE:超卖只会在月底盘点时暴露
现象:发药高峰期两位药师同时在窗口发同一盒药,库存显示 1 盒,两个处方都提示发药成功。月底盘点,库里少了 1 盒,翻流水怎么都对不上。
原因:发药逻辑写成了“先 SELECT 判断库存够不够,再 UPDATE 扣减”。两条语句之间没有锁,并发时两个请求都看到库存 1,都判断“够”,然后都执行扣减,互相覆盖。
解决:把扣减写成一条原子语句:UPDATE drug_stock SET quantity = quantity - 1 WHERE drug_id = ? AND batch_no = ? AND quantity >= 1,然后用受影响行数判断是否扣成。如果还要同时写流水,就必须放在同一个事务里。这个改动成本极低,但能直接把超卖概率降为零。
5.4 时间字段存成字符串:跨院区查询时区直接乱掉
现象:某院区导出的挂号记录时间比实际晚 8 小时,按天分组的统计全部错位。
原因:设计文档里 visit_time 字段类型写的是 VARCHAR,写入时程序本地拼接字符串,不同院区服务器时区不一致,存进去的就是错的时间。
解决:所有时间一律用 DATETIME 或 TIMESTAMP,应用层统一时区,数据库连接串指定 serverTimezone。另一个容易忽略的是“只存某一天”的业务字段,比如挂号的 visit_date,直接用 DATE,不要用 DATETIME 再套 DATE() 函数去取,否则索引直接失效。
注意:如果时间字段已经是 VARCHAR 的老库,迁移时用 STR_TO_DATE 洗一遍,别试图在查询层用函数兼容,那样索引永远用不上。
5.5 外键建太多:导入十万条历史数据卡到怀疑人生
现象:把老系统数据导入新库,10 万条挂号记录导了 4 个小时,中途还因为一条孤儿记录外键报错中断。
原因:设计文档里给每张子表都写了物理 FOREIGN KEY。导入时逐条校验外键、还要按主表到子表的顺序,十万条数据放大十倍代价。老系统里本来就有“患者档案已删、挂号记录还在”的孤儿数据,物理外键直接拦截。
解决:生产库用逻辑外键(只建索引不建约束),数据清洗阶段把孤儿记录单独标记归档。设计文档里外键照画,但要在“约束说明”列写明:物理外键仅适用于开发环境,生产环境删除。课设数据量小,物理外键没问题,但养成这个习惯以后接真实项目能省很多事。
6. 交付前的验证 SQL 与清单:十分钟查出设计文档里的隐藏问题
设计文档写完不等于设计完成,我会在上线前跑三条验证 SQL 和一份十项检查清单,十分钟内把文档里的隐藏问题暴露出来。
6.1 三条验证 SQL
-- 1. 查出绑定了就诊记录却找不到患者档案的孤儿数据 SELECT r.reg_id, r.patient_id FROM registration r LEFT JOIN patient p ON p.patient_id = r.patient_id WHERE p.patient_id IS NULL; -- 2. 对账:挂号明细金额汇总不等于收费汇总,账就平不了 SELECT r.patient_id, SUM(r.fee) AS reg_fee FROM registration r WHERE r.status IN (0, 1) GROUP BY r.patient_id; -- 3. 库存不能为负,出现负数说明发药逻辑没做原子扣减 SELECT drug_id, batch_no, quantity FROM drug_stock WHERE quantity < 0;这三条 SQL 执行的顺序,就是设计的验收顺序:先验数据缝合得紧不紧,再验钱平不平,最后验库存逻辑有没有并发漏洞。任何一条查出异常,都说明设计文档对应章节要返工,而不是修数据。
6.2 十项检查清单
| 检查项 | 判定标准 |
|---|---|
| 主键是否语义无关 | 所有业务表主键是 ID,不是证件号/手机号 |
| 金额与数量字段 | 全部 DECIMAL,不允许 FLOAT |
| 时间字段类型 | 时间用 DATETIME/TIMESTAMP,日期用 DATE |
| 业务记录能否 DELETE | 挂号、处方、费用记录只允许状态流转 |
| 外键策略 | 文档描述逻辑外键,生产脚本不带物理外键 |
| 高频查询有无对应索引 | 挂号号源、患者历史、医生排班都有联合索引 |
| 状态字段是否字典化 | gender/status 用 TINYINT 并注释枚举值 |
| 收费是否有流水 | 费用表有结算状态,退费走状态不删记录 |
| 患者能否合并档案 | patient 独立主索引,重复建档有合并机制 |
| 权限边界 | sys_user 角色与业务表操作权限一一对应 |
我的习惯是交文档前把这份清单打印出来逐项打勾。以前我总跳过“业务记录能否 DELETE”这一项,觉得退号删记录多干净,直到被报表数据打脸才改过来。现在任何一张业务表,我都会先问一句:这一行要是删了,月底的报表还准吗?不准就只改状态、不删行。这个习惯救了我很多次,希望帮到你。
本文还有配套的精品资源,点击获取