简介:面向医院信息科、EMR实施运维人员及医疗信息化研究者的卫宁电子病历表结构文档,系统梳理临床信息系统5.0的数据库设计,可用于数据对接、报表开发、接口排错与二次开发时快速定位表与字段。文档覆盖800余张数据表,逐表给出说明、字段定义、类型、长度、备注及值域,并延伸至职工、科室、病区等基础代码库,收费与医保分类,药品及诊断编码等模块,体现数据标准化与互操作设计。资源包共1个doc文件,约13.92MB,适合按目录检索和查阅。已有1554人学习下载,说明其在医疗信息化从业者中具有一定参考基础。读者可据此理解表间关系与编码规则,减少对接歧义,并为数据治理、质量监控和临床数据分析提供可操作的字段级依据。
1. 从一份 800 张表的说明书说起:电子病历表结构到底解决什么问题
接手一套跑了十年的院内系统,要在检查申请单上加一个科室盖章位,代码里搜不到入口,最后翻出一份 5.0 版定稿的电子病历表结构说明书,顺着CPOE_RIS_BRJCSQD主表和CPOE_RIS_BRJCSQDMX明细表摸下去,半小时就定位到打印模板落在哪个库里。这类文档不教你怎么写代码,它告诉你这家医院的数据长什么样。
这份说明书覆盖 800 多张表,分「系统框架」和「医生工作站」两大块,每张表给出中文说明、字段名、字段类型、长度、备注和值域,从职工、科室、药品、收费、医保这些公共字典,一路铺到医嘱、小处方、手术、输血、血库审核这些业务表。
适合三类人:要对接 EMR 数据做报表或集成的开发者、要在院内做二次开发的实施同学、想搞清楚临床数据模型长什么样的架构同学。拿到它之后真正难的不是读,是知道从哪张表开始读、哪些字段能信、哪些坑文档里没写。
2. 前缀即契约:SYS_ / PUB_ / CPOE_ / OUTP_ 四类表的分工
一份 800 张表的清单直接从头翻到尾,翻到第 200 张就会失去方向。真正高效的做法是先看前缀,前缀基本决定了这张表归谁维护、能不能跨模块改、数据是从业务里长出来的还是人工维护的。这套系统的命名不是随便起的,SYS_、PUB_、CPOE_、OUTP_四类前缀加两三位拼音缩写,构成了一套相当克制的约定。
2.1 四类前缀的职责边界
SYS_前缀对应的是系统级的组织架构骨架,文档 1.1、1.4、1.5 三节就是它的全部内容:SYS_ZGDMK职工代码库、SYS_KSDMK科室代码库、SYS_BQDMK病区代码库。这三张表是全院所有业务的外键源头——一条医嘱必须知道是哪个医生开的、开在哪个科室、落在哪个病区。它们的变更频率极低,通常由信息科统一维护,普通业务模块只有读权限。
PUB_前缀是公共字典层,从 1.2 的PUB_YLXMK医疗项目库一直排到 1.45 的PUB_GWYPJBDMK同步高危药品级别库,是整份文档里条目最多的一段。这类表的特点是全院共享、只增不删、用有效标志代替物理删除。药品分类、诊断代码、手术五件套(等级/诊断/部位/麻醉/类别)、ICD9/ICD10、地区区县国家代码,全都堆在这一层。做二次开发时最常打交道的就是它——新增一个收费项目、挂一个医保分类,改的都是PUB_表。
CPOE_前缀占了文档第 2 大章 60 多个条目,是医生工作站(Computerized Physician Order Entry)的录入侧。医嘱、小处方、申请单、手术协议、输血同意书、排班记录都在这里。这些表的数据是医生一条条点出来的,写入频繁,字段多,主从关系复杂,是整份文档里最值得细读的部分。
OUTP_前缀是门诊侧,数量很少,只有OUTP_MZYZPCK门诊医嘱频次、OUTP_MZYZYFK门诊医嘱用法、OUTP_PSYPDYK皮试药品对应、OUTP_YGZDK导入院感诊断这四张。少不代表不重要,恰恰说明门诊系统有独立的一套库,这里只列了与住院侧共用的那部分字典。
| 前缀 | 职责 | 典型表 | 维护方 | 读写特征 |
|---|---|---|---|---|
SYS_ | 组织架构 | SYS_KSDMK科室 | 信息科 | 读多写极少 |
PUB_ | 全院公共字典 | PUB_YPFLK药品分类 | 药剂科/医保办 | 读多写少 |
CPOE_ | 医生工作站业务 | CPOE_XCFK小处方 | 临床科室 | 读写频繁 |
OUTP_ | 门诊共用字典 | OUTP_MZYZYFK门诊用法 | 门诊部 | 读多写少 |
2.2 后缀也有语义:K / DYK / MXK / MBK / SZ 怎么读
前缀分完类,第二层信息藏在后缀里。文档里绝大多数表以K结尾,就是「库」的意思,等同于一张物理表。但有几类后缀含义更具体,认出来能省掉大量猜测。
DYK是对应库,也就是映射表,用来把两套编码体系接起来。PUB_KSYFDYK科室药房对应、PUB_LCSFXMDYK临床收费项目对应、CPOE_YZPCYFDYK医嘱频次用法对应、CPOE_YBYPDYK医保药品对应、CPOE_YZSXTJDYK医嘱时限条件对应,全部属于这一类。这类表的字段通常就两三个编码列,主键往往是两列联合,改动风险高——改一条映射,可能让某个科室的药房取不到药。
MXK是明细库,一定挂在某张主表下面。CPOE_XCFMXK小处方明细挂CPOE_XCFK,CPOE_SJLBMXK数据类别明细挂CPOE_SJLBK,CPOE_ZDFLMXK诊断分类明细挂CPOE_ZDFLK,CPOE_XKSHMXK血库审核明细挂血库审核主表。看到MXK就要去找它的主表,单查明细表基本没有业务意义。
MBK/MB是模板库。CPOE_RIS_SQDMBK申请单模板、CPOE_SSJLMB手术记录模板、CPOE_SSXYMB手术协议模板,这些表存的是带占位符的文本或结构化片段,是全院统一文书格式的落点。做打印定制基本绕不开这里。
SZ结尾表示设置表,存的是开关和规则而非业务数据。CPOE_SSYZK_SZ手术医嘱设置、CPOE_XCF_SZ小处方设置、CPOE_YZDYSZ_EX医嘱对应设置(_EX是扩展表)。这些表往往一个科室一行或者一个院区一行,出问题时先看这里有没有被谁改过。
2.3 字段清单四件套:说明、类型长度、备注、值域
每张表下面列出的字段信息是四列结构:字段名、中文说明、类型长度、备注与值域。前三列好理解,真正决定能不能干活的是第四列。
值域列有三种写法,含义完全不同。第一种直接写死枚举,比如有效标志写「1-有效 0-无效」;第二种写引用关系,比如「见 PUB_YPFLK」;第三种是空的,注释都没填。第一种最省心,照着建枚举就行;第二种要求你先确认引用表的主键和过滤条件,因为引用表里通常还有一堆停用数据;第三种最耗时间,只能靠生产数据SELECT DISTINCT反推,或者找业务方确认。
类型长度这一列也有个历史包袱需要注意。2014 年前后的院内系统大多跑在 GBK 字符集下,一个中文占两个字节,VARCHAR(20)实际能放 10 个汉字。迁到 UTF-8 之后同样的长度只能放 6 个汉字多一点,药品名、科室名这类字段特别容易超长。文档不会告诉你这件事,但如果你直接照抄长度建库,导入历史数据时会看到成片截断。
提示:读字段清单时先扫「值域」列,把空的挑出来单独列一张表,这部分就是你需要额外确认的工作量,往往比写代码本身更花时间。
3. 基础字典层落地:职工、科室、收费、医保四组核心表怎么建
PUB_和SYS_加起来四十多张字典表,全建一遍既没必要也不现实。真实项目里通常按依赖顺序推进:先建组织架构,再建收费和药品,最后建医保对照。顺序错了会很痛苦——收费项目要挂科室,医保要挂收费项目和药品,倒着做就得反复改表。
3.1 SYS_ZGDMK 与 SYS_KSDMK:人员与科室的组织骨架
这两张表是整份文档里被引用次数最多的。文档只给了字段清单,落到 DDL 大致是下面这个样子,我按院内系统的常见做法做了补全,字段名以文档为准,类型和默认值可以按你们的实际情况调。
-- 科室代码库:全院唯一,门诊/住院/医技共用一套编码 CREATE TABLE SYS_KSDMK ( KSDM VARCHAR(10) NOT NULL, -- 科室代码,业务表里引用的就是它 KSMC VARCHAR(60) NOT NULL, -- 科室名称,会出现在单据打印上 YJKS VARCHAR(10), -- 一级科室,对应 PUB_YJKSK EJKS VARCHAR(10), -- 二级科室,对应 PUB_EJKSK KSLX CHAR(1), -- 科室类型:0临床 1医技 2药房 3行政 PYM VARCHAR(20), -- 拼音码,前台下拉框模糊检索用 YXBZ CHAR(1) DEFAULT '1', -- 有效标志:1启用 0停用 PXH INT, -- 排序号,决定下拉框里的展示顺序 PRIMARY KEY (KSDM), KEY IDX_KSDM_PYM (PYM) -- 拼音码必须建索引,否则下拉框会卡 ); -- 职工代码库:医生、护士、技师统一在这张表 CREATE TABLE SYS_ZGDMK ( ZGDM VARCHAR(10) NOT NULL, -- 职工代码,医嘱表里的开单医生字段指向它 ZGXM VARCHAR(30) NOT NULL, -- 职工姓名 KSDM VARCHAR(10), -- 所属科室,外键到 SYS_KSDMK ZCBM VARCHAR(10), -- 职称编码,外键到 PUB_ZCBMK YSJB CHAR(1), -- 医生级别:决定能不能开某些限制级药品 PYM VARCHAR(20), -- 拼音码 YXBZ CHAR(1) DEFAULT '1', -- 有效标志 PRIMARY KEY (ZGDM), KEY IDX_ZGDM_KS (KSDM) );KSDM是全院主键,长度给到 10 位是留了余量,实际院内编码一般 4 到 6 位。YJKS和EJKS这两个字段对应文档 1.34 的PUB_YJKSK一级科室库和 1.35 的PUB_EJKSK二级科室库,构成「一级科室 → 二级科室 → 科室」的三层结构,做科室维度的统计报表时按这三层汇总,比按KSDM硬分组要清晰得多。
SYS_ZGDMK里有个容易忽略的YSJB医生级别字段。这东西直接关联处方权限,不同级别能开的药品范围不一样,做权限校验时不能只看角色表,得回来查这里。
3.2 PUB_SFDXMK 与 PUB_SFXMLBK:收费项目的两级分类链
收费这块文档给了五张表,容易看晕,理一下关系就清楚了:PUB_SFDXMK收费大项目库和PUB_SFXMLBK收费项目类别库负责分类,PUB_SFXXMK导入收费小项目库存明细,PUB_LCSFXMK临床收费项目库是临床侧看到的视图,PUB_LCSFXMDYK临床收费项目对应库把临床项目和收费小项目接起来。
-- 收费大项目:床位费、检查费、检验费这类粗分类 CREATE TABLE PUB_SFDXMK ( DXMDM VARCHAR(10) NOT NULL, -- 大项目代码 DXMMC VARCHAR(40) NOT NULL, -- 大项目名称 SFXZ CHAR(1), -- 收费性质:0收入 1成本 YXBZ CHAR(1) DEFAULT '1', PRIMARY KEY (DXMDM) ); -- 收费小项目:真正带价格的明细项 CREATE TABLE PUB_SFXXMK ( SFXM VARCHAR(20) NOT NULL, -- 收费项目代码,费用明细表引用的就是它 SFMC VARCHAR(80) NOT NULL, -- 收费项目名称,发票上打印这个 DXMDM VARCHAR(10) NOT NULL, -- 所属大项目,分组统计用 MLDM VARCHAR(10), -- 所属类别,对应 PUB_SFXMLBK DJ DECIMAL(12,4), -- 单价,注意小数位给到 4 位 DW VARCHAR(10), -- 单位:次/日/瓶 YBXH VARCHAR(30), -- 医保序号,和医保目录对照用 YXBZ CHAR(1) DEFAULT '1', PRIMARY KEY (SFXM), KEY IDX_SFXM_DX (DXMDM) );DJ单价字段小数位给到 4 位不是随手写的。院内很多耗材按「每毫升」「每毫克」计价,两位小数根本不够,做费用计算时中间结果也不能提前ROUND,得算完总额再取整,否则会出现几分钱的对不上账。这是血泪经验,对账对到凌晨的时候才想起来查这个字段的精度。
YBXH医保序号是收费项目和医保目录之间的软关联,实际对照关系落在CPOE_YBXMDYK里。两张表都要维护,前者是给收费员看的参考值,后者才是结算时真正用的。
3.3 PUB_KSYFDYK 与 PUB_MZSFZXKSK:两列复合主键的对应表
对应表的建法跟普通字典表不太一样,主键通常是两列联合,而且往往没有自增 ID。以科室药房对应为例,一个科室可以对应多个药房,一个药房也可以服务多个科室,典型的多对多。
-- 科室药房对应:决定医生开药时能选哪个药房的药 CREATE TABLE PUB_KSYFDYK ( KSDM VARCHAR(10) NOT NULL, -- 科室代码 YFDM VARCHAR(10) NOT NULL, -- 药房代码,对应 PUB_YFDMK MRBZ CHAR(1) DEFAULT '0', -- 默认标志:1是该科室的默认发药药房 PXH INT, PRIMARY KEY (KSDM, YFDM) );这个MRBZ默认标志是关键。医生开医嘱时如果没手动选药房,系统会取默认药房,取错了就会导致药房摆药列表里出现不属于自己的药。常见做法是在应用层保证「一个科室最多一条MRBZ='1'」,但数据库层面加不了唯一约束(因为要允许多条'0'),只能靠触发器或者定时任务校验。生产上这个约束被破坏过不止一次,建议在科室药房维护界面上直接拦。
PUB_MZSFZXKSK门诊收费执行科室对应和PUB_ZYSFZXKSK住院收费执行科室对应是同一套逻辑换个场景:一项检查在开单科室开,但实际执行(也就是产生成本)在另一个科室,这两张表定义了这个映射。做科室成本核算的时候如果不走这张表,算出来的成本全是错的。
3.4 字典表通用六件套:编码、名称、拼音码、有效标志、排序号、停用日期
把四十多张PUB_表横向对比一遍,会发现它们共享一套几乎一模一样的字段组合,我把它叫六件套:编码、名称、拼音码、有效标志、排序号、停用日期。这不是巧合,是这套系统刻意做的统一。
| 字段 | 常见命名 | 作用 | 注意点 |
|---|---|---|---|
| 编码 | XXDM/XXBH | 业务外键 | 一旦被引用就不能改,只能停用重建 |
| 名称 | XXMC | 界面显示、单据打印 | 历史单据打印的是当时的名称,需要快照 |
| 拼音码 | PYM/SRM | 下拉框模糊检索 | 必须建索引,排序规则用二进制更稳 |
| 有效标志 | YXBZ | 逻辑删除 | 1 有效 0 停用,但存在反向定义的表 |
| 排序号 | PXH | 下拉框顺序 | 空值排序在 MySQL 里排最前,要处理 |
| 停用日期 | TSRQ | 记录停用时间 | 与YXBZ经常不一致,见第 5 章 |
拼音码这块有个实际取舍。院内用户习惯打首字母,比如输入「nek」找「内科」,所以PYM存的是全拼首字母。但重码率很高,同一个拼音码可能对应十几条记录,常见做法是同时存一个五笔码或者输入码字段做二次过滤。文档里有些表列了SRM(输入码),有些没列,做二次开发时统一补上会更省事。
停用日期和有效标志并存是个历史遗留设计。理想状态是停用时同时写YXBZ='0'和TSRQ=当前日期,实际数据里经常只有YXBZ被改了,TSRQ还是空的。做「某时间段内有效的科室列表」这种查询时,两个字段都要判断,别只信一个。
4. 医生工作站主线:一条医嘱从录入到打印经过哪些表
CPOE_这一段有 60 多个条目,硬读会迷失。最有效的切入方式是跟着一条业务走:医生开一条医嘱,点了保存,数据落到哪几张表;点了打印,模板从哪来;护士执行,状态字段怎么变。跟着走一遍,剩下的表基本都是这条链路的变种。
4.1 医嘱基础四件套:YZLXK、YZYPYFK、YZYPPCK、YZPCYFDYK
医嘱能不能正常开出来,取决于四张基础表配得对不对。CPOE_YZLXK医嘱类型库定义有哪些医嘱类型(长期/临时/护理/检查/检验),CPOE_YZYPYFK医嘱药品用法库定义给药途径(口服/静脉滴注/肌注),CPOE_YZYPPCK医嘱药品频次库定义频次(qd/bid/tid/q8h),CPOE_YZPCYFDYK医嘱频次用法对应库把频次和用法绑在一起。
-- 医嘱药品频次库:医生开药时下拉框里那一列 CREATE TABLE CPOE_YZYPPCK ( PCDM VARCHAR(10) NOT NULL, -- 频次代码,如 QD、BID、Q8H PCMB VARCHAR(20) NOT NULL, -- 频次名称,打印在医嘱单上 MRCS INT, -- 每日次数,用于计算当日总用量 ZQ INT, -- 周期天数,qod 这类隔日医嘱填 2 JYBS CHAR(1) DEFAULT '0', -- 仅白天标志,某些频次限定白天执行 YXBZ CHAR(1) DEFAULT '1', PRIMARY KEY (PCDM) ); -- 频次用法对应:同一种频次在不同给药途径下的执行时间点不同 CREATE TABLE CPOE_YZPCYFDYK ( PCDM VARCHAR(10) NOT NULL, -- 频次代码 YFDM VARCHAR(10) NOT NULL, -- 用法代码 ZXSJ VARCHAR(100), -- 执行时间点,如 "08:00,12:00,16:00" PRIMARY KEY (PCDM, YFDM) );MRCS每日次数这个字段是计算用量的基础。医生录入「阿莫西林 0.5g bid」,系统要算出当日总量 1.0g,靠的就是MRCS=2。如果频次维护成Q8H,MRCS应该是 3,但如果有人图省事填了 2,医嘱单上的总量就会错。这类配置错误不会报错,只会让数据慢慢偏掉,做数据核对时特别难查。
CPOE_YZPCYFDYK的ZXSJ执行时间点字段用逗号分隔存多个时间,这种设计在 2014 年是合理的(省一张表),但查询时要拆字符串,做「某个时间点该执行哪些医嘱」的需求会很难写。二次开发时常见的做法是新建一张执行时间明细表,把ZXSJ拆开放进去,然后写同步任务保持两边一致。
另外还有几张扩展表值得留意。CPOE_YZYLKZXXK医嘱用量扩展信息库存的是用量与体重/体表面积相关的计算参数,儿科用得最多。CPOE_YZSXTJDYK医嘱时限条件对应库定义哪些医嘱有时限(比如临时医嘱多久自动停止)。CPOE_SSYZK手术医嘱库和它的设置表CPOE_SSYZK_SZ是手术场景的特殊医嘱,跟普通医嘱走不同的流程。
4.2 小处方 CPOE_XCFK 与 XCFMXK 的主从结构
小处方是门诊场景下的常用功能,医生把几个常用药打包成一个处方模板,一键开出去。文档里给了三张表:CPOE_XCFK小处方主表、CPOE_XCFMXK小处方明细、CPOE_XCF_SZ小处方设置。
-- 小处方主表:一个处方模板一行 CREATE TABLE CPOE_XCFK ( XCFBH VARCHAR(20) NOT NULL, -- 小处方编号 XCFMC VARCHAR(60) NOT NULL, -- 小处方名称,如"上呼吸道感染套餐" KSDM VARCHAR(10), -- 归属科室,为空表示全院共用 ZGDM VARCHAR(10), -- 创建人 SYCS INT DEFAULT 0, -- 使用次数,用于排序展示常用处方 YXBZ CHAR(1) DEFAULT '1', PRIMARY KEY (XCFBH) ); -- 小处方明细:一个模板里的每一味药 CREATE TABLE CPOE_XCFMXK ( XCFBH VARCHAR(20) NOT NULL, -- 小处方编号,外键到主表 XH INT NOT NULL, -- 序号,主从表联合主键的一部分 YPDM VARCHAR(20) NOT NULL, -- 药品代码 YFDM VARCHAR(10), -- 用法 PCDM VARCHAR(10), -- 频次 YL DECIMAL(12,4), -- 单次用量 YLDW VARCHAR(10), -- 用量单位 YCTS INT, -- 用药天数 PRIMARY KEY (XCFBH, XH) );主从结构里最容易翻车的是XH序号。它既是联合主键的一部分,又承担界面展示顺序的职责。医生在界面上删掉中间一行,后面要不要重排?常见做法是不重排,新插入取MAX(XH)+1,这样已打印的单据顺序不会变。但如果有人在维护界面上做了「上移下移」,就会触发批量更新主键,动作大了容易锁表。做二次开发时建议加一个独立的PXH排序字段,把主键职责和展示职责分开。
SYCS使用次数这个字段值得单独说。它没有存在明细表里,而是在主表上做累加,目的是让医生打开小处方列表时按使用频率排序取前十个。这个字段在高并发门诊场景下会有写冲突,常见做法是异步累加或者干脆定时统计重算。
4.3 检查申请单:BRJCSQD 主表与三类附属表
文档 2.3 到 2.9 这一小段是整份说明书里主从关系最完整的一组。CPOE_RIS_BRJCSQD病人检查申请单是主表,CPOE_RIS_BRJCSQDMX检查申请单明细是项目明细,CPOE_RIS_BRJCSQDFSXXK申请单附属信息存附加字段,再往上是三张模板和配置表:CPOE_RIS_SQDMBK申请单模板库、CPOE_RIS_SQDSJFLK数据分类库、CPOE_RIS_SQDSJK数据库、CPOE_RIS_SQDWZMBK文字模板库。
-- 检查申请单主表:一次申请一行 CREATE TABLE CPOE_RIS_BRJCSQD ( SQDH VARCHAR(20) NOT NULL, -- 申请单号,全局唯一 BRID VARCHAR(20) NOT NULL, -- 病人 ID ZYH VARCHAR(20), -- 住院号,门诊申请时为空 KSDM VARCHAR(10), -- 开单科室 KDYS VARCHAR(10), -- 开单医生,对应 SYS_ZGDMK.ZGDM SQSJ DATETIME, -- 申请时间 JYJ CHAR(1), -- 加急标志 1加急 SQZT CHAR(1), -- 申请状态:0新开 1已提交 2已执行 3已取消 PRIMARY KEY (SQDH), KEY IDX_SQD_BR (BRID, SQSJ) ); -- 申请单明细:一次申请可以含多个检查项目 CREATE TABLE CPOE_RIS_BRJCSQDMX ( SQDH VARCHAR(20) NOT NULL, -- 申请单号 XH INT NOT NULL, -- 序号 SFXM VARCHAR(20) NOT NULL, -- 检查项目代码,对应 PUB_SFXXMK.SFXM BW VARCHAR(40), -- 检查部位,如"胸部正位" SM VARCHAR(200), -- 临床说明,给医技科室看的 PRIMARY KEY (SQDH, XH) );SQZT申请状态字段是这条链路的核心。状态机不走完,检查科室就看不到这张申请单。实际项目里状态推进是分散的:医生站提交改一次,医技科室接单改一次,出报告再改一次。如果某个环节的代码漏写了状态更新,单子就卡在中间态,医生这边显示「已提交」,医技那边看不到,两边互相甩锅。做问题排查时第一件事就是SELECT SQZT, COUNT(*) FROM CPOE_RIS_BRJCSQD GROUP BY SQZT,看哪个状态堆积异常。
SM临床说明字段长度要留意。文档里给的长度普遍偏紧,实际业务里医生填的说明经常上百字,特别是要求「结合临床」的时候。建库时适当放宽到 500 甚至TEXT更保险。
4.4 医保对照:YBYPDYK、YBXMDYK、YBYPBXBLDYK 的三层映射
医保这块文档给了三张对应表,逻辑上是三层。CPOE_YBXMDYK医保项目对应把院内收费项目映射到医保目录项;CPOE_YBYPDYK医保药品对应把院内药品映射到医保药品目录;CPOE_YBYPBXBLDYK医保药品报销比例对应定义每个药品在不同医保类型下的自付比例。
-- 医保药品对应:院内药品编码 -> 医保药品编码 CREATE TABLE CPOE_YBYPDYK ( YPDM VARCHAR(20) NOT NULL, -- 院内药品代码 YBYPBH VARCHAR(30) NOT NULL, -- 医保药品编号 YBLX VARCHAR(10) NOT NULL, -- 医保类型:职工/居民/离休等 JYBZ CHAR(1) DEFAULT '1', -- 甲乙类标志:1甲类 2乙类 3丙类 YXBZ CHAR(1) DEFAULT '1', PRIMARY KEY (YPDM, YBLX) ); -- 报销比例:同一药品在不同险种、不同人群下比例不同 CREATE TABLE CPOE_YBYPBXBLDYK ( YBYPBH VARCHAR(30) NOT NULL, -- 医保药品编号 YBLX VARCHAR(10) NOT NULL, -- 医保类型 BXBL DECIMAL(6,4), -- 报销比例,0.8 表示自付 20% ZFBL DECIMAL(6,4), -- 自付比例 PRIMARY KEY (YBYPBH, YBLX) );在主键里放YBLX医保类型是个关键设计。同一盒药,职工医保可能报 80%,居民医保只报 50%,如果对应表只按药品编码做主键,就表达不了这层差异。做医保结算接口时,先按(YPDM, YBLX)查出医保编号,再按(YBYPBH, YBLX)查报销比例,两次查询的YBLX必须一致,中间断了就会出现「药品对上了但比例取错」的问题。
JYBZ甲乙类标志直接影响患者自付金额,这个字段填错的后果比想象中大。甲类全额进统筹,乙类先自付一部分再进统筹,丙类全自费。常见做法是在药品字典维护界面强制要求选择,不允许留空,但从文档看不出有没有做这个约束,接手时最好先跑一遍空值统计。
5. 避坑清单:拿到这份表结构文档后最容易翻车的 5 个地方
文档写得再细,也是 2014 年的静态快照。真正上手之后会发现,坑不在字段缺失,而在文档没写的那部分约定。下面五条是我在这类院内系统上真踩过的,每条按「现象 → 原因 → 解决」写。
5.1 表名与字段层面的三个坑
坑一:表名是 RIS,中文名却写「检验申请单」。
现象:按文档中文名去建模块,把CPOE_RIS_BRJCSQD当成检验申请单处理,结果对接 LIS 系统时字段全对不上。
原因:RIS是放射信息系统的缩写,这套申请单结构最早是从放射科系统沿用过来的,后来扩展到检查科室,中文说明只改了一部分没改全,文档 2.6 那节写「检验申请单模板库」,实际表名还是CPOE_RIS_SQDMBK。
解决:一切以表名和字段为准,中文说明只作参考。判断申请单属于检查还是检验,看明细表SFXM关联的收费项目大类,不要看表名注释。
坑二:OUTP_PSYPDYK 和 CPOE_PSYPDYK 同名不同前缀。
现象:写皮试药品查询时随手挑了一张,上线后发现门诊能查到、住院查不到,或者反过来。
原因:文档 1.41 是OUTP_PSYPDYK,2.2 是CPOE_PSYPDYK,两张都是「皮试药品对应信息」,分别服务门诊和住院两套流程,字段结构相近但数据不同步。
解决:先确认业务场景归属,门诊侧读OUTP_,住院侧读CPOE_。如果要做统一的皮试校验,得自己建一张合并视图并处理两张表的差异字段。
坑三:值域列写着 0/1,但不同表里 0/1 含义相反。
现象:按一张表养成的习惯写判断,YXBZ = '1'在 A 表是「有效」,到 B 表变成了「停用」,查出来的数据全是反的。
原因:文档里有效标志大部分是「1 有效 0 无效」,但确实存在个别表反向定义,而且备注列写得很含糊。
解决:每接一张新表,先跑一次SELECT YXBZ, COUNT(*) FROM 表名 GROUP BY YXBZ,结合业务量判断哪个值是有效。把这个结论记在自己的字段字典里,别靠记忆。
5.2 关联查询与数据迁移的两个坑
坑四:明细表复合主键,删中间行后序号不重排导致打印错位。
现象:医生删掉处方里第三味药,打印出来的处方序号是 1、2、4,护士看着别扭,追问是不是系统有问题。
原因:主从表用(单据号, 序号)做联合主键,序号承担了主键和展示两个职责,删除时不敢重排是因为怕影响已打印单据的历史追溯。
解决:不重排是合理的,但展示层要按当前记录重新编号,用ROW_NUMBER()或者应用层计数器生成显示序号,别直接把XH印在纸上。
-- 展示层重新编号,XH 保持不动 SELECT ROW_NUMBER() OVER (ORDER BY XH) AS 显示序号, YPDM, YL, YFDM FROM CPOE_XCFMXK WHERE XCFBH = 'XC2024001';坑五:字段长度按 GBK 字节算,迁到 UTF-8 后成片截断。
现象:把历史数据从老库导到新库,药品名、科室名、诊断名称大量被截断,导入日志里一堆Data too long。
原因:文档里的长度是 2014 年按 GBK 设计的,VARCHAR(20)在 GBK 下能放 10 个汉字,UTF-8 下只有 6 个多。文档不会标字符集,光看长度以为是字符数。
解决:迁移前把所有VARCHAR长度按「原长度 × 0.6」估算一遍,凡是存中文名称的字段统一放大到 1.5 到 2 倍。更稳的做法是先用MAX(LENGTH(字段))/3统计实际最长值再定长度,比拍脑袋靠谱。
注意:这五条里最容易被低估的是坑三和坑五,前者不会报错只会让数据错得悄无声息,后者在迁移当天才爆发,留给你修的时间只有几个小时。
6. 二次开发:把 .doc 变成可查询的元数据服务
这份文档是 Word 格式,八百多张表翻起来靠 Ctrl+F 找表名,效率极低。我现在的习惯是第一步就把它转成结构化数据,落地成一张元数据表,之后所有查询都走 SQL。
import subprocess, docx # 第一步:.doc 先转 .docx,Word 的表格 python-docx 读不了旧格式 subprocess.run(["libreoffice", "--headless", "--convert-to", "docx", "--outdir", "./out", "emr_schema.doc"], check=True) # 第二步:遍历所有表格,识别「表名 + 字段清单」的块 doc = docx.Document("./out/emr_schema.docx") rows = [] current_table = None for table in doc.tables: for row in table.rows: cells = [c.text.strip() for c in row.cells] # 字段清单一般是 4 列:字段名 / 说明 / 类型长度 / 值域 if len(cells) >= 3 and cells[0].isupper() and "_" in cells[0]: rows.append((current_table, cells[0], cells[1], cells[2], cells[3] if len(cells) > 3 else "")) elif len(cells) == 1 and "K" in cells[0]: current_table = cells[0].split()[0] # 识别表名行这段脚本的核心是两个判断:表名行通常只有一个单元格且以K结尾,字段行通常第一列是全大写加下划线。实际文档排版不规整,识别率大概八成,剩下的靠人工补。libreoffice --headless的--outdir参数指定输出目录,转换时必须确保没有 Word 进程占用源文件。
元数据进库之后,最实用的一个用途是做结构体检。把文档里的字段定义和生产库的information_schema对一遍,能一次性找出所有「文档有、库里没有」和「库里多了、文档没写」的字段。
-- 生产库里某张表的真实结构 SELECT COLUMN_NAME, DATA_TYPE, CHARACTER_MAXIMUM_LENGTH, IS_NULLABLE FROM information_schema.COLUMNS WHERE TABLE_SCHEMA = 'emr' AND TABLE_NAME = 'CPOE_RIS_BRJCSQD' ORDER BY ORDINAL_POSITION; -- 与元数据表比对,找出定义漂移 SELECT m.field_name, m.data_type AS 文档类型, c.DATA_TYPE AS 实际类型 FROM meta_emr_column m LEFT JOIN information_schema.COLUMNS c ON c.TABLE_SCHEMA = 'emr' AND c.TABLE_NAME = m.table_name AND c.COLUMN_NAME = m.field_name WHERE m.table_name = 'CPOE_RIS_BRJCSQD' AND (c.COLUMN_NAME IS NULL OR c.DATA_TYPE <> m.data_type);第二段 SQL 是这套方法的真正价值所在。文档是 2014 年的,生产库跑了十年,中间加过多少字段没人说得清。每次接手一个新模块,我都先跑一遍这个比对,把漂移字段列出来,再去问业务方每个字段是干什么的。比翻代码快得多,也比问人靠谱。
有一回我没做这一步,直接照文档建了一张字典表的实体类,上线后某个字段一直取到空值。查了两小时才发现生产库里那个字段被改名了,文档还是老名字,代码里也没写日志。从那以后我每次接新模块,都强制先把文档解析成元数据表,再跑一遍结构比对,确认每个字段都在正确的位置上再动手写代码。
希望帮到你。
本文还有配套的精品资源,点击获取