ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

卫宁电子病历表结构拆解:HIS对接与SQL查询实战指南

卫宁电子病历表结构拆解:HIS对接与SQL查询实战指南 简介卫宁电子病历表结构文档面向医院信息管理者、IT技术人员及医疗信息化研究人员系统梳理了卫宁EMR 5.0的数据库设计框架帮助读者理解临床信息系统的数据模型与标准化规范。资源包内含1个doc文件约13.92MB完整收录800余张数据表的说明、字段定义、类型、长度及值域备注。内容覆盖系统框架、财务收费、医疗信息与数据标准化等模块如职工代码库、医疗项目库、药品分类库、科室与病区代码库以及凭证类型库、收费大项目库、医保分类库、诊断代码库、职称编码库等并涉及权限管理与数据加密等安全设计。已有1543人学习下载适合用于优化系统结构、排查数据交换问题及开展医疗大数据分析是掌握卫宁EMR底层表结构的重要参考。1. 卫宁电子病历表结构.doc一份让 HIS 对接少走弯路的拆解思路手上拿到一份「卫宁电子病历表结构.doc」多数人的第一反应是翻目录找字段但真正做过医院信息系统对接的都知道这份文档的价值不在字段清单本身而在于它决定了你后面写 SQL、做视图、跑数据同步时会不会踩到坑。卫宁健康的电子病历产品线在国内三级医院占有率很高围绕它的表结构做二次开发、数据抽取、质控分析是很多医疗 IT 从业者的日常。这篇文章不讲空泛概念而是把这份表结构文档拆成能直接落地的路径先搞清楚它的组织逻辑再讲怎么从文档里提取关键表关系最后落到查询、同步和避坑的具体操作。适合正在做电子病历数据对接、质控指标开发或临床数据仓库建设的工程师。2. 卫宁电子病历表结构的组织逻辑从文档到数据模型2.1 先分清三类表主表、明细表、字典表拿到卫宁电子病历表结构文档不要从头到尾逐行读。我一般先按表名后缀和字段特征把它分成三类这个分类直接决定后面怎么写 JOIN。第一类是主表通常以PATIENT、VISIT、EMR开头或包含MASTER、RECORD字样存放一次就诊或一份病历的核心标识。第二类是明细表表名里常带DETAIL、ITEM、ENTRY一条主记录对应多条明细比如医嘱明细、检验项目明细。第三类是字典表表名多含DICT、CODE、TYPE用来做编码翻译比如性别代码、科室代码、诊断编码。为什么先做这个分类因为卫宁的表结构文档通常按模块排列但实际查询时你需要跨模块 JOIN。如果不先分清主从关系很容易写出笛卡尔积。一个典型场景查某个患者某次住院的所有检验结果你需要从就诊主表拿到VISIT_ID再去检验明细表按VISIT_ID过滤最后 JOIN 字典表翻译检验项目名称。三步走缺一步结果就不完整。提示文档里如果看到XX_ID同时出现在两张表里大概率是外键关联字段优先记下来。2.2 关键字段的命名规律与含义卫宁电子病历表结构里字段命名有比较固定的套路。掌握这些规律你甚至可以在没有文档的情况下猜出七八成。PATIENT_ID是患者主索引通常全院唯一但注意有些老版本用PAT_NO或MRN。VISIT_ID是就诊流水号一次住院或一次门诊对应一个这是最常用的关联键。VISIT_TYPE区分门诊、急诊、住院值通常是1、2、3或O、E、I具体看文档里的字典说明。EMR_ID是电子病历文档主键一份病历可能对应多个EMR_ID比如入院记录、病程记录、手术记录各一个。时间字段要特别留意。CREATE_TIME是记录创建时间UPDATE_TIME是最后修改时间但卫宁有些表用RECORD_TIME表示临床发生时间和系统时间不是一回事。做质控指标时比如「入院记录 24 小时内完成」你要用的是临床时间字段不是系统创建时间。这个坑我见过不止一次查出来的数据对不上最后发现是时间字段选错了。2.3 从文档到 ER 图手工梳理表关系的步骤文档不会直接给你 ER 图但你可以自己画。步骤不复杂关键是耐心。第一步把文档里所有表名抄到一张纸上或 Excel 里按模块分组。第二步逐表标注主键字段通常文档里会标PK或加粗。第三步找外键如果 A 表的某个字段和 B 表的主键同名或明显相关画一条线。第四步标注关系类型一对多、多对多、一对一。这个过程听起来笨但比直接上手写 SQL 靠谱。我一般会重点梳理三条链路患者-就诊-病历、就诊-医嘱-医嘱明细、就诊-检验-检验结果。这三条链路覆盖了电子病历数据抽取 80% 的需求。梳理完之后你会得到一张自己的 ER 图后面写查询就是按图索骥。3. 从表结构文档到可执行 SQL查询与抽取实战3.1 用 VISIT_ID 串联就诊与病历的最小查询假设你要查某个患者最近一次住院的入院记录内容。文档里告诉你就诊信息在PAT_VISIT表病历主表在EMR_DOCUMENT病历内容在EMR_CONTENT。下面是最小可用查询-- 查询指定患者最近一次住院的入院记录 SELECT v.VISIT_ID, v.VISIT_TYPE, v.ADMISSION_TIME, d.EMR_ID, d.DOC_TYPE, -- 文档类型入院记录通常为 ADMISSION_NOTE c.CONTENT_TEXT -- 病历正文可能是 CLOB 或 TEXT 类型 FROM PAT_VISIT v JOIN EMR_DOCUMENT d ON v.VISIT_ID d.VISIT_ID JOIN EMR_CONTENT c ON d.EMR_ID c.EMR_ID WHERE v.PATIENT_ID 你的患者ID AND v.VISIT_TYPE I -- I 表示住院 AND d.DOC_TYPE ADMISSION_NOTE ORDER BY v.ADMISSION_TIME DESC FETCH FIRST 1 ROW ONLY; -- 不同数据库写法不同Oracle 用 ROWNUM这段 SQL 的逻辑很直接先从就诊表按患者和就诊类型过滤拿到住院记录再关联病历文档表按文档类型筛出入院记录最后关联内容表取正文。参数方面VISIT_TYPE的值一定要查文档里的字典表确认不同医院实施时可能配成1或I。DOC_TYPE同理有的环境用中文编码有的用英文缩写。FETCH FIRST是标准 SQL 写法Oracle 老版本要用WHERE ROWNUM 1SQL Server 用TOP 1。注意EMR_CONTENT表可能很大正文是 CLOB 字段查询时不要SELECT *只取需要的列。3.2 医嘱明细的层级展开与过滤条件医嘱数据是电子病历里结构最复杂的部分之一。卫宁的表结构通常把医嘱拆成三层医嘱主表ORDERS、医嘱明细表ORDER_ITEMS、医嘱执行表ORDER_EXECUTE。主表存医嘱的开立时间、开立医生、医嘱类型明细表存具体的药品或项目执行表存每次执行的时间和执行人。查一个患者住院期间的所有长期医嘱SQL 大概长这样-- 查询住院期间的长期医嘱及明细 SELECT o.ORDER_ID, o.ORDER_TYPE, -- 长期/临时文档里查字典 o.START_TIME, o.DOCTOR_NAME, i.ITEM_NAME, i.DOSAGE, i.FREQUENCY FROM ORDERS o JOIN ORDER_ITEMS i ON o.ORDER_ID i.ORDER_ID WHERE o.VISIT_ID 目标就诊号 AND o.ORDER_TYPE LONG -- 长期医嘱具体值查字典 AND o.STATUS ACTIVE -- 有效医嘱避免已停止的 ORDER BY o.START_TIME;这里的关键参数是ORDER_TYPE和STATUS。ORDER_TYPE在不同版本里可能是0/1、L/T或长期/临时必须查文档字典。STATUS字段用来过滤已停止或已作废的医嘱如果不加这个条件你会把历史停嘱也查出来导致统计偏差。ORDER_ITEMS表里ITEM_NAME可能是编码需要再 JOIN 药品字典或项目字典翻译成名称。3.3 检验结果与病历文本的关联查询质控和科研场景经常需要把检验结果和病历文本关联起来比如查某个患者血糖异常时对应的病程记录。卫宁的表结构里检验主表通常是LAB_TEST结果明细在LAB_RESULT病历文本在EMR_CONTENT。关联键是VISIT_ID加时间范围。-- 查询血糖异常前后 24 小时内的病程记录 SELECT l.TEST_NAME, l.RESULT_VALUE, l.REPORT_TIME, e.DOC_TYPE, e.CREATE_TIME, c.CONTENT_TEXT FROM LAB_TEST l JOIN LAB_RESULT r ON l.TEST_ID r.TEST_ID JOIN EMR_DOCUMENT e ON l.VISIT_ID e.VISIT_ID JOIN EMR_CONTENT c ON e.EMR_ID c.EMR_ID WHERE l.VISIT_ID 目标就诊号 AND l.TEST_NAME LIKE %血糖% AND r.RESULT_VALUE 11.1 AND e.CREATE_TIME BETWEEN l.REPORT_TIME - INTERVAL 24 HOUR AND l.REPORT_TIME INTERVAL 24 HOUR AND e.DOC_TYPE PROGRESS_NOTE ORDER BY l.REPORT_TIME, e.CREATE_TIME;这段查询的难点在时间范围关联。INTERVAL是 PostgreSQL 和 Oracle 的写法MySQL 用DATE_SUB和DATE_ADD。另外LAB_TEST和LAB_RESULT可能是一对多一个检验项目多条结果JOIN 之后要注意去重。EMR_DOCUMENT和EMR_CONTENT也可能一对多如果一份病历有多个版本需要按CREATE_TIME取最新版本。提示跨表时间关联时先确认所有时间字段的时区和精度卫宁有些表存的是DATE有些是TIMESTAMP混用会丢精度。4. 卫宁电子病历表结构对接的避坑与排查4.1 字段值不统一同一含义在不同表里编码不同现象写好的查询在 A 医院跑得通到 B 医院结果为空。原因卫宁不同项目实施时字典编码可能被本地化修改。比如性别字段有的环境存1/2有的存M/F有的存男/女。解决不要硬编码值先查DICT或CODE类字典表或者从文档的「值域说明」章节确认。我一般会写一个字典查询子查询把编码翻译成统一值再过滤。4.2 大表查询超时EMR_CONTENT 和 LAB_RESULT 的索引陷阱现象查询病历正文或检验结果时SQL 跑几分钟不出结果。原因EMR_CONTENT和LAB_RESULT是典型的大表几百万甚至上千万行如果VISIT_ID或EMR_ID上没有索引全表扫描必然超时。解决先确认文档里标注的索引字段查询时务必带上索引列作为过滤条件。如果文档没标用EXPLAIN看执行计划必要时让 DBA 加索引。另外CLOB 字段不要放在WHERE里做LIKE性能极差。4.3 时间字段混用CREATE_TIME 和临床时间的区别现象统计「入院记录 24 小时内完成率」时数据对不上。原因用了CREATE_TIME而不是临床记录时间。卫宁有些表的CREATE_TIME是系统写入时间可能比实际书写时间晚几小时甚至几天。解决查文档里有没有RECORD_TIME、WRITE_TIME、CLINICAL_TIME这类字段优先用临床时间。如果文档没写清楚找实施人员确认或者对比几条已知数据反推。4.4 关联字段类型不一致隐式转换导致索引失效现象JOIN 查询很慢但单独查两张表都很快。原因关联字段类型不一致比如 A 表VISIT_ID是VARCHAR2B 表是NUMBER数据库做隐式转换索引失效。解决查文档确认字段类型写 JOIN 时显式转换比如ON a.VISIT_ID TO_CHAR(b.VISIT_ID)。但更好的做法是让 DBA 统一类型或者建函数索引。4.5 文档版本与数据库不一致字段已废弃或新增现象按文档写的字段数据库里报「无效标识符」。原因文档版本落后于实际数据库或者医院做了定制化修改。解决先用DESC 表名或查询USER_TAB_COLUMNS确认实际字段再对照文档。如果发现文档没有的字段不要贸然使用先找实施确认含义。我一般会维护一份「文档 vs 实际」的差异表每次对接新医院先跑一遍字段比对。5. 把表结构文档变成可复用的数据资产5.1 建立自己的元数据表字段映射与血缘记录文档是死的数据库是活的。我习惯在对接完一家医院后把关键表的字段映射、字典值、关联关系整理成一张元数据表存在自己的数据库里。表结构大概这样CREATE TABLE MY_META_FIELD_MAP ( SOURCE_TABLE VARCHAR(100), -- 源表名 SOURCE_FIELD VARCHAR(100), -- 源字段 TARGET_TABLE VARCHAR(100), -- 目标表名 TARGET_FIELD VARCHAR(100), -- 目标字段 TRANSFORM_RULE VARCHAR(500), -- 转换规则如字典翻译 REMARK VARCHAR(500) -- 备注如版本差异 );这张表的好处是下次对接同版本卫宁系统时直接查映射关系不用再翻文档。TRANSFORM_RULE字段记录字典翻译逻辑比如SEX: 1-男, 2-女。REMARK记录版本差异比如「V5.0 用 VISIT_IDV6.0 改用 ENCOUNTER_ID」。这个习惯帮我省了大量重复劳动。5.2 用视图封装复杂关联让业务查询不再碰底层表底层表结构复杂业务人员或分析师直接查容易出错。我一般会建几个核心视图把常用的关联和字典翻译封装进去。比如CREATE VIEW V_PATIENT_VISIT_EMR AS SELECT v.PATIENT_ID, v.VISIT_ID, v.VISIT_TYPE, v.ADMISSION_TIME, v.DISCHARGE_TIME, d.EMR_ID, d.DOC_TYPE, c.CONTENT_TEXT FROM PAT_VISIT v LEFT JOIN EMR_DOCUMENT d ON v.VISIT_ID d.VISIT_ID LEFT JOIN EMR_CONTENT c ON d.EMR_ID c.EMR_ID;视图的好处是屏蔽底层复杂性业务查询只需要SELECT * FROM V_PATIENT_VISIT_EMR WHERE PATIENT_ID xxx。但要注意视图里的LEFT JOIN可能带来性能问题如果底层表很大建议加物化视图或定时刷新。另外视图不要嵌套太深否则排查问题很痛苦。5.3 验证方法用已知病例反查数据完整性做完对接后怎么验证查出来的数据是对的我的习惯是找几个已知病例手工核对。具体做法选一个住院患者从 HIS 系统里导出他的入院记录、病程记录、检验报告然后用自己的查询跑一遍对比字段值和内容。重点核对三类数据时间字段是否一致、字典翻译是否正确、明细记录是否漏查。如果发现差异先查是不是过滤条件太严再查是不是 JOIN 丢了记录。我遇到过LEFT JOIN写成INNER JOIN导致没有检验记录的患者被过滤掉的情况这种错误在统计总体指标时影响很大。验证通过后把查询固化下来作为后续开发的基准。5.4 一个具体技巧用文档目录反推模块优先级卫宁电子病历表结构文档通常有目录目录顺序往往反映了模块的重要性或使用频率。我一般会先看目录里哪些模块被放在前面比如「患者基本信息」「就诊信息」「病历文档」通常在前这些就是核心表优先梳理。后面的「质控」「科研」「接口」模块按需处理。另外文档里的「修订记录」或「版本说明」页很关键它会告诉你哪些表在哪个版本新增或废弃。如果对接的是老版本系统直接跳过新增表省时间。如果文档没有修订记录就对比数据库实际表清单差异部分重点确认。提示文档里的示例 SQL 或查询片段不要直接抄往往和实际环境有出入只作参考。这些年做卫宁电子病历对接最大的教训就是别信文档的「完整性」。文档写的是标准版医院跑的是定制版中间差着无数实施细节。我现在的习惯是拿到文档先跑一遍字段比对再找实施确认字典值最后用真实病例验证。三步走完后面写查询才踏实。希望帮到你。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进