
简介面向计算机专业数据库课程设计与毕业设计的医院管理系统建库参考文档围绕医院信息系统HIS的实际业务流程展开帮助读者理清从需求分析到表结构落地的完整思路。内容以门诊与住院两大部门的组织架构为线索梳理挂号、收费、药房领药、检查科室、血库、病案与手术室等环节的数据流转并给出数据完整性、一致性、安全性与并发性能等设计要点配合 ER 图与实体表、外键关联说明可依此推导病人表、科室表、医生表、药品表等核心结构。压缩包内为 1 个 docx 文档约 398KB正文含需求分析、部门业务流程图示与字段设计说明适合直接作为课程作业模板或需求梳理底稿。已有 175 人学习下载可供缺乏医院业务背景的读者快速建立领域认识并对照完善自己的数据库方案。1. 一份能直接建库的字典比一张好看的 E-R 图值钱计算机专业做数据库课程设计医院管理系统几乎是出现频率最高的题目之一可交上去的东西往往是一张 E-R 图加几张表结构截图真要建库、灌数据、跑一遍业务流当场就卡住。这份《医院管理系统数据库设计.docx》不一样的地方在于它没有停在“病人和医生是多对多关系”这种层面而是把门诊、住院两条主线上的每一张单据拆成了可命名的数据项、数据结构、数据流和数据存储挂号号、病历号、处方号、医嘱号、手术序号全部给了 char(8) 的定长编码和归属流程。它解决的核心问题是让你知道该建几张表、每张表挂在哪个业务节点上、哪个字段是被谁写入又被谁读走。适合正在做课设毕设、要给小型医疗机构做数据层、或者想搞清楚 HIS 业务建模到底长什么样的从业者拿去对照改造。2. 门诊与住院两条主线把业务流程翻译成数据流2.1 门诊链路的五个节点每一步都在写表门诊这条线在文档里被拆得很清楚挂号处登记并产生挂号单收费处划价收费并落到收费项目医生问诊产出门诊病历、门诊处方或检查申请单病人拿着单据回到收费处二次缴费最后药房凭处方发药、检查科室凭申请单出结果报告。这条链路看似线性实际上每一步都对应一次数据库写入和一次状态推进想不清楚就会在后面被反复返工。第一个节点是挂号。文档里挂号单的数据结构包含挂号号、挂号类别、主治医师、挂号人姓名、挂号科室、挂号日期六项。初诊病人要在这里录入姓名、年龄、住址、联系方式复诊病人直接调已有档案。这就意味着挂号单和病人信息是两张表挂号单只存病人号做外键不能把姓名年龄冗余进去——一旦冗余病人改了手机号历史挂号记录里的旧号码就永远对不上。第二个节点是收费。文档把收费拆成一条独立的数据流处方信息和检查单都是先到收费处再分流到药房或检查科室。这个设计是对的收费项目单独一张表处方号、检查序号都只作为收费项目的关联单据号存在于是同一笔费用既能挂处方也能挂检查还能挂挂号费。很多自己动手的人会图省事把金额直接写在处方表里结果门诊退费、检查退费、挂号退费三种场景各写一套逻辑改一次要动三处。第三个节点是处方与检查单的分叉。医生开处方走药房开检查单走检查科室两条路在结构上高度对称都有一个自增性质的单据号处方号 Cf_no、检查序号 Jc_no都挂主治医师都有内容字段和时间字段。文档里门诊处方和检查项目是两张表而不是一张“医嘱表”加类型字段这个选择在门诊场景下更稳妥因为处方要对接库存检查要对接设备排期后续的字段分化会非常快。第四个节点是药房发药。处方信息这条数据流里数据来源写的是门诊医生和收费处数据流向是药房这其实隐含了一个状态机处方从“已开具”到“已缴费”再到“已发药”每一步都要能查出来。设计上至少要有开单时间、缴费时间、发药时间三个时间戳而不是一个 flag。第五个节点是复诊收口。病人拿到检查结果回到原科室复诊医生基于结果开处方或给出医疗建议最终痊愈离院。这一步对应的是门诊病历的追加写——病历号 Bl_no 不变诊断时间 zd_date 继续往后追加记录所以门诊病历表的主键不能是病历号得是病历号加诊断时间的组合或者干脆另加一个流水主键。提示门诊这条线最容易漏的是“二次缴费”这一步。检查申请单在开单时就要能估价否则收费处没法划价而估价依赖检查项目字典表字典表要提前建好。2.2 住院链路是一台状态机不是一串流水账住院部分比门诊复杂原因不在表多而在于同一个病人在同一时间点上状态是唯一的但历史上的状态必须全部可追溯。文档里住院业务的起点是住院处办理入院手续、登记基本信息、缴纳预交款或押金终点是病区出院登记加住院处办结出院中间穿插排床、转床、执行医嘱、发药、检查、手术、转科。把这条链路画成状态机核心状态大约是已登记 → 已排床在科 → 手术中临时 → 在科 → 已出科 → 已出院。每一次状态迁移都对应一张单据入院登记产生住院号 Zy_no 和病区号 Bq_no排床产生病床号 Bc_no 的占用记录转床要保留原床位记录并新开一条手术申请单进手术室后要回写手术通知单和麻醉记录出院要写病案。病房床位这张表是典型的强一致热点。文档里病床号的类型是 char(8)但没有明说床位状态怎么存。我的做法是床位表只存物理属性病床号、所属病区、房间号、床位类型占用关系单独一张“床位占用记录表”字段是病床号、住院号、开始时间、结束时间。结束时间为空表示当前占用。这样转床、出院、历史查询三件事全解决了也不用在床位表上反复 UPDATE 状态字段——那种写法在并发排床时几乎必然出现两个病人抢同一张床。医嘱是住院链路的另一个重心。文档里医嘱的数据结构包含诊断单序号、诊断类别、启动日期、处理日期、医嘱内容、主治医师、病人。这里要注意“启动日期”和“处理日期”是两个概念启动日期是医嘱开始生效的时间处理日期是护士实际执行的时间。长期医嘱每天执行一次如果只有一条医嘱记录那执行记录必须单独一张表否则就会变成把同一条医嘱复制几十份后期统计用药量时根本没法区分“开了多少次”和“执行了多少次”。手术环节中文档提到手术室需将麻醉记录、术中医嘱、材料器械使用记录在案。这三类数据形态完全不同麻醉记录是时间序列给药时间、剂量、生命体征材料器械是明细行物品编号、数量、单价术中医嘱是文本加执行确认。硬塞进一张手术表会立刻失控正确拆法是手术主表存手术序号、主刀医师、住院号、手术名称、手术日期、手术结果、手术室麻醉记录和材料消耗各建子表用手术序号做外键。药品请领这条线也值得单独说。病区科室把医嘱里的用药分类汇总形成药品申领单统一向药库领药然后把药按时按量发给住院病人并记录发药情况。这里存在三级库存药库总库、药房或病区分库、病人消耗端。文档把药库的药库号、负责人、类别、面积作为独立数据结构是有道理的——药库是采购和存储主体药房是发放主体两者的库存必须分开记账否则药房向药库领了 100 盒药药库库存该不该减减了之后药房没发完药去哪了答案是要有一次“库间调拨”的流水才能对得上账。2.3 组织架构决定数据边界别把所有科室都建成表文档列出的科室非常多口腔科、内科、外科、皮肤科、骨科、药库、药房、设备科、财务科、后勤仓库、门诊收费处、门诊挂号处、问讯处、住院处、检查科室、血库、病案室、手术室、行政部门。新手看到这一串的第一反应通常是“建一张科室表全部塞进去”这没错但边界划分比表本身更重要。我的划分方式是三层。第一层是业务实体层只有病人、医生、药品、床位、检查项目这几类真正被反复引用的对象建主表。第二层是单据层挂号单、病历、处方、检查单、医嘱、手术单、请领单、采购单、收费单每张单据表都带一个指向业务实体的外键和一个时间戳。第三层是字典层科室、职称、药品规格、检查类别、收费项目类型这些低频变更但高频引用的内容统一进字典表主表只存字典编码。问讯处、行政部门、后勤仓库这类在文档里出现但没有实质数据流的部门不必单独建表。它们的存在意义是说明组织完整性落到数据库上最多是科室字典里的一行挂个类型标记就够了。文档在“确定系统的边界”一节里其实已经提示了这一点原始数据录入、数据更新、失效数据删除、日常维护由手工完成其余统计工作交给计算机。这句话翻译成设计语言就是数据库不负责业务流程的推进只负责状态的持久化和一致性的守卫谁在什么时候把数据写进来必须有明确的责任主体。注意别把“检查科室”和“检查项目”混成一个概念。科室是组织单位检查项目是收费和出报告的单元一个科室能做几十种检查两者是一对多。3. 从数据字典落到建表语句char(8) 主键与约束怎么定3.1 数据项到列类型长度、别名与命名文档给出的数据项类型非常统一几乎所有业务编号都是 char(8)订单号是 char(20)病区号是 char(10)金额类用 int日期用 date内容类用 char(100)。这套类型定义在当年是合理的放到今天建库需要做几处调整但调整要有理由不能全盘推翻。文档定义建议落地类型调整理由挂号号 char(8)CHAR(8) 保留定长编码便于打印和扫码长度固定不浪费病历号 char(8)CHAR(8) 保留同上且病案室归档要求编码稳定订单号 char(20)VARCHAR(20)订单号可能分段拼接定长会有尾随空格困扰病区号 char(10)CHAR(4) 或 VARCHAR(8)10 位对病区编码冗余按实际编码规则收紧费用 int(10)DECIMAL(10,2)int 存金额无法表达角分退费对账必然出问题处方内容 char(100)VARCHAR(500)一条处方多味药100 字符装不下病人年龄 int(3)改为出生日期 DATE年龄会随时间变化存年龄等于埋一个定时炸弹年龄这一条值得单独强调。文档里门诊病人和病人信息两张结构都存了年龄这在短期演示里没问题但系统跑满一年后所有病人的年龄全部错位。正确做法是只存出生日期年龄由查询时用日期函数算出来需要展示年龄的视图里现算。命名上文档混用了拼音缩写和英文比如 gh_no、bl_no、cf_no 和 br_name、zd_date 并存。落地时建议统一策略业务编号沿用拼音缩写这和纸质单据、窗口打印一致改了对不上普通字段用英文。别名和实际列名之间要维护一张映射表放在设计文档里别指望后来人靠猜。金额用 int 这件事必须说清楚。文档里挂号费、药品费、检查费都是 int(10)单位大概是元。一旦出现 0.5 元的找零或者按比例分摊的退费取整误差就会累积月底财务对账差几分钱查一整天是常态。改成 DECIMAL(10,2) 之后所有加减乘除都走定点运算误差可控。3.2 核心表建表语句与主键选择下面这几张表覆盖了门诊链路最关键的几个节点。注意每张表都显式声明了主键、非空约束和指向字典表或实体表的外键这些约束不是装饰是后面排查数据错乱的唯一线索。-- 病人主表只存相对稳定的基本信息年龄不落库 CREATE TABLE patient ( people_no CHAR(8) NOT NULL COMMENT 病人号8位唯一编码, br_name VARCHAR(32) NOT NULL COMMENT 病人姓名, br_sex CHAR(1) NOT NULL COMMENT 性别 M/F, birthday DATE NULL COMMENT 出生日期年龄由此计算, id_card CHAR(18) NULL COMMENT 身份证号唯一索引, addr VARCHAR(120) NULL COMMENT 家庭住址, tel VARCHAR(20) NULL COMMENT 联系方式, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (people_no), UNIQUE KEY uk_id_card (id_card) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT病人基本信息; -- 挂号单只引用病人号绝不冗余姓名 CREATE TABLE register ( gh_no CHAR(8) NOT NULL COMMENT 挂号号, people_no CHAR(8) NOT NULL COMMENT 病人号外键, gh_type VARCHAR(16) NOT NULL COMMENT 挂号类别初诊/复诊/急诊, dept_code VARCHAR(16) NOT NULL COMMENT 挂号科室编码, doctor_no CHAR(8) NULL COMMENT 主治医师号普通号可为空, gh_fee DECIMAL(8,2) NOT NULL DEFAULT 0.00 COMMENT 挂号费, gh_date DATETIME NOT NULL COMMENT 挂号时间, status TINYINT NOT NULL DEFAULT 0 COMMENT 0已挂号 1已就诊 2已退号, PRIMARY KEY (gh_no), KEY idx_people_date (people_no, gh_date), KEY idx_dept_date (dept_code, gh_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT门诊挂号单;挂号单的两个索引不是随手加的。idx_people_date服务于“这个病人最近来过几次”的查询idx_dept_date服务于科室按日统计门诊量。文档里门诊挂号的数据量估的是每年 10000 张这个量级单表完全够用不需要分区但索引必须建对否则年底做科室工作量报表时会全表扫描。-- 门诊处方主键是处方号业务上允许一张处方多行明细 CREATE TABLE prescription ( cf_no CHAR(8) NOT NULL COMMENT 处方号, bl_no CHAR(8) NOT NULL COMMENT 关联病历号, people_no CHAR(8) NOT NULL COMMENT 病人号, doctor_no CHAR(8) NOT NULL COMMENT 开方医师, cf_date DATETIME NOT NULL COMMENT 开方时间, cf_fee DECIMAL(10,2) NOT NULL DEFAULT 0.00 COMMENT 处方金额合计, pay_status TINYINT NOT NULL DEFAULT 0 COMMENT 0未缴费 1已缴费 2已退费, dispense_status TINYINT NOT NULL DEFAULT 0 COMMENT 0未发药 1已发药, PRIMARY KEY (cf_no), KEY idx_people_cfdate (people_no, cf_date) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT门诊处方主表;处方主表和处方明细表必须分开。文档里处方内容是一个 char(100) 的大字段演示够用实际业务里一张处方五六味药每味药还要带规格、数量、单价、用法用量塞进一个文本字段之后就再也统计不出“本月某药品消耗量”这种数字了。明细表用cf_no药品编号做主键天然防重复录入。-- 药品库存库房和药房分开记账用库房类型区分 CREATE TABLE drug_stock ( stock_id BIGINT NOT NULL AUTO_INCREMENT, drug_no CHAR(8) NOT NULL COMMENT 药品编号, store_type CHAR(2) NOT NULL COMMENT 库房类型 CK库房 YF药房 BF病区, store_code VARCHAR(16) NOT NULL COMMENT 库房或病区编码, qty INT NOT NULL DEFAULT 0 COMMENT 当前库存数量, safety_qty INT NOT NULL DEFAULT 0 COMMENT 安全库存低于触发预警, update_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, PRIMARY KEY (stock_id), UNIQUE KEY uk_drug_store (drug_no, store_type, store_code), KEY idx_warn (store_type, qty) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT药品分库库存;这张表的关键是唯一键uk_drug_store。它保证同一药品在同一库房只有一条记录库存变更走 UPDATE 加数量而不是 INSERT 新行。库间调拨则必须写两条 UPDATE 加一条调拨流水放在同一个事务里任何一步失败全部回滚。3.3 外键、检查约束与索引的取舍很多团队建表时把外键全删了理由是“影响性能”。在门诊挂号这种高频写入的表上确实要谨慎但完全不要外键的代价是数据质量失控——处方表里出现一个不存在的病人号事后只能靠人工比对。我的折中做法是核心实体之间的主从关系用外键约束处方对病人、医嘱对住院号、明细对主表纯统计类、日志类的表不建外键靠应用层校验加定时对账脚本兜底。检查约束在 MySQL 8.0 之后是可用的值得用在几个地方性别只能是 M 或 F金额不能为负数状态字段限定在枚举范围内。这几条约束的价值在于它们能在数据入库的那一刻拦住错误而不是等到报表跑出来发现有一笔负数收费才知道出事了。索引方面按文档给出的业务量估算挂号、病历、检查单、医嘱各每年一万条三年也就三万条级别这个量级下索引的目的是消除全表扫描而不是调优。真正需要关注的是复合索引的字段顺序等值条件在前范围条件在后日期字段尽量放在复合索引的末尾。4. 业务一致性库存扣减、三单对账与单号生成4.1 库存扣减的三种写法与选择标准药品库存扣减是整个系统里最容易出并发问题的地方。药房发药、药库出库、病区领药三个动作可能同时发生写法不对就会出现超发或者负库存。第一种写法是读-改-写先 SELECT 出当前库存应用层判断够不够再 UPDATE 成新值。这种写法在单线程演示里永远正确一上并发就翻车两个事务同时读到 100各扣 10最后库存变成 90 而不是 80。第二种写法是数据库端原子更新把判断和扣减合成一条语句靠影响行数判断成功与否-- 原子扣减只有库存足够时才会更新成功 UPDATE drug_stock SET qty qty - 10 WHERE drug_no YP000012 AND store_type YF AND store_code YF001 AND qty 10; -- 影响行数为 0 说明库存不足应用层回滚整个事务这种写法简单、可靠不需要显式加锁InnoDB 的行锁会保证同一行的更新串行化。它的局限是只能处理单条扣减如果一张处方有多味药需要把多条 UPDATE 放在同一个事务里任何一条影响行数为 0 就整体回滚。第三种写法是先写流水再算余额即每次出入库都插一条流水记录库存数量由流水汇总得出。这种写法可追溯性最好任何时点的库存都能重算出来代价是查询要聚合量大之后需要定时把历史流水归档成期初余额。我的建议是两者结合日常扣减用第二种保证实时准确同时写一条流水用于审计和对账两边定期比对不一致就报警。4.2 处方、收费、发药的三单对账文档里的数据流把处方信息定义为“由门诊医生开出处方经收费处流向药房”这条链路在数据库层面其实是三张表三个状态处方表的 pay_status 从 0 变 1dispense_status 从 0 变 1收费表的金额合计要等于处方金额合计。三单对账的检查逻辑可以写成一条 SQL定期跑一遍把不一致的单据捞出来-- 找出已缴费但金额与收费记录不一致的处方 SELECT p.cf_no, p.cf_fee, COALESCE(SUM(c.amount), 0) AS paid_fee FROM prescription p LEFT JOIN charge_item c ON c.biz_type CF AND c.biz_no p.cf_no AND c.refund_flag 0 WHERE p.pay_status 1 GROUP BY p.cf_no, p.cf_fee HAVING p.cf_fee COALESCE(SUM(c.amount), 0);这段 SQL 里biz_type和biz_no是收费明细的多态关联字段一张收费表同时挂处方、检查单、挂号单三种单据。多态关联的缺点是无法建外键所以必须配一个对账任务每天凌晨跑一次把不一致的单据导出来人工核对。这个后悔药我建议每个做 HIS 数据层的人都提前备好出问题时能省下大量翻日志的时间。4.3 单号生成别用 max(id)1文档里所有业务编号都是 8 位字符编码这说明编号规则不是自增整数。实际实现时有三种做法数据库序列、应用层雪花算法、按日期加序号的业务规则。SELECT MAX(gh_no)1这种写法是最常见也最危险的一种。高峰期两个挂号窗口同时执行拿到同一个最大值插入时主键冲突用户看到的是“挂号失败请重试”。更隐蔽的问题是一旦出现删除操作MAX 会回退导致编号重复使用。正确做法是用独立的编号序列表加行锁取号-- 取号事务先锁行再加一保证同一窗口不会拿到重复号 BEGIN; SELECT cur_val INTO v FROM seq_no WHERE seq_name GH FOR UPDATE; UPDATE seq_no SET cur_val cur_val 1 WHERE seq_name GH; COMMIT; -- 应用层把 v 补齐到 8 位前补零后作为挂号号FOR UPDATE会把这一行锁住直到事务提交并发取号自然串行化。缺点是取号成了串行点吞吐受限。8 位编码空间是 1 亿按每天几千次挂号算几十年用不完所以不需要考虑回绕。如果业务量真的到了取号成为瓶颈的程度再改成按窗口号分段的号段模式每个窗口预先取 1000 个号在本地发用完再取。5. 避坑与排查五个踩过才知道疼的地方5.1 用姓名做关联重名直接串档现象两个同名病人的病历混在一起医生打开病历时看到的是别人的检查结果。原因为了“看着直观”在挂号单和病历表里冗余存了病人姓名关联查询时用姓名做条件。中文姓名重名率远比直觉高一个中等规模医院里同名同姓的病人一年能碰上几十个。解决所有跨表关联一律走编码姓名只作为展示字段且展示时带上病人号后四位。建表时就禁止用 name 类字段做外键这条规矩要写进设计规范。5.2 金额用浮点或整数存对账永远差几分现象药房发药金额合计和收费处收费合计差 0.01 到 0.03 元反复核对单据又都对。原因金额字段用 FLOAT 或者用 INT 存元按比例分摊退费时产生舍入误差误差在多次累加后放大。解决统一改 DECIMAL(10,2)分摊算法用“最大余额法”把余数分配给最后一笔保证分项之和恒等于总额。已经上线又改不动的加一张对账差异表把每次误差记下来单独核销。5.3 药房和药库共用一张库存表请领单变成糊涂账现象药房向药库领了 100 盒药药库库存减了药房库存没加系统显示药品凭空消失。原因库存表只有药品编号一个维度没有库房维度。药房和药库的操作互相覆盖后写的把先写的冲掉了。解决库存表加库房类型和库房编码两个维度唯一键改成药品编号库房类型库房编码。库间调拨必须写两条更新加一条调拨流水包在同一个事务里。5.4 病案和门诊病历合成一张表出院归档后查不到现象病人出院后病案室按病案号查不到住院期间的完整记录只能查到门诊部分。原因把门诊病历和住院病案当成同一种东西共用一张表。门诊病历是按次追加住院病案是一次归档两者的生命周期、查询方式、保留期限都不同强行合并后归档逻辑无处安放。解决两张表分开建病案表用病案号做主键关联住院号出院时由病区触发归档写入。门诊病历保持按次追加两者通过病人号关联需要全量视图时用视图或者宽表拼。5.5 char(8) 比较时尾随空格导致查不到现象用 GH000123 去查能查到用参数传进来的 GH000123 去查查不到。原因CHAR 是定长类型存储时右侧补空格比较时如果一方是 VARCHAR 或者参数带空格等值判断可能不成立。这个问题在不同数据库、不同字符集下的表现还不完全一致。解决所有业务编号在应用层统一做去空格加补零处理入参先 trim 再补齐到 8 位。建表时如果确定编码长度不会变用 CHAR 没问题如果编码规则可能扩展直接用 VARCHAR 更省心。6. 用造数脚本和 EXPLAIN 把设计跑一遍设计文档写完不等于设计成立。我现在的习惯是任何一份表结构定稿前先写一个造数脚本按文档里给出的年数据量放大十倍灌进去挂号单 10 万、病历 10 万、医嘱 10 万、处方明细 40 万然后用真实的业务查询去打它。这个动作花不了半小时但能提前暴露九成以上的索引缺失和类型错误。造数脚本用 Python 写最省事关键是让数据分布贴近真实医生集中在少数几个热门科室挂号时间集中在上午八点到十一点药品消耗集中在几十种常见药上。均匀分布的数据是最会骗人的它会让所有索引看起来都很快。import random, string, datetime def gen_no(prefix, seq): 生成 8 位业务编号2位前缀 6位序号不足补零 return f{prefix}{seq:06d} def gen_register(n): rows [] base datetime.datetime(2024, 1, 1, 8, 0, 0) for i in range(1, n 1): # 80% 的挂号集中在上午 8-11 点制造真实的并发热点 if random.random() 0.8: t base datetime.timedelta( daysrandom.randint(0, 364), minutesrandom.randint(0, 180)) else: t base datetime.timedelta( daysrandom.randint(0, 364), hoursrandom.randint(8, 16)) rows.append(( gen_no(GH, i), gen_no(BR, random.randint(1, 60000)), # 病人号允许复诊重复 random.choice([初诊, 复诊, 急诊]), random.choice([NK, WK, PFK, YYK, EYK]), # 科室编码热门科室权重更高 gen_no(YS, random.randint(1, 400)), round(random.choice([5, 10, 15, 20, 50]), 2), t, 0 )) return rows这段脚本的重点不在语法而在两个参数病人号从 6 万人的池子里随机取保证复诊病人占多数科室编码只给五个模拟真实的科室分布不均。灌完数据之后跑三条查询按病人查历史挂号、按科室按日统计门诊量、按医生统计工作量每条前面加 EXPLAIN看 type 是不是 ref 或 range看 rows 估算和实际差多少。如果出现 ALL说明索引没建对如果 rows 估算是 1 实际扫了 8 万行说明统计信息过期跑一次 ANALYZE TABLE。进阶一点的做法是把文档里那几个处理过程写成存储过程或者定时任务比如“库存低于安全库存时自动生成采购建议”“出院未归档病案超过三天自动提醒”然后让造数脚本连续跑七天的业务量观察库存预警表和待办表会不会暴涨。设计阶段能想到的异常路径最好都在这一步用数据压出来。最后说个我自己的教训。早些年做一套类似的系统表结构设计得很漂亮字段类型、索引、约束全都对唯独没做造数验证上线第一个月就碰上月末结算财务那边三万多条收费记录跑对账跑了四十分钟原因是收费表按 biz_type 和 biz_no 建了联合索引但查询里用了 DATE(create_time) 做条件函数包住列直接让索引失效。从那以后我每次定稿表结构之前都必须先灌十万条脏数据、跑一遍真实查询、看一眼执行计划少一步都不放心。希望这套流程能帮到你。本文还有配套的精品资源点击获取