
简介浙江联众卫生信息科技有限公司出品的电子病历EMR表结构说明文档面向医院信息化管理员、系统运维与二次开发人员旨在帮助读者理解并高效管理电子病历相关数据库结构。文档由占贵顺、葛向东多次修订内容覆盖ZJHIS、EMR3、EMR及EMRJK等应用场景从基础用户信息到临床数据的关键表均有涉及。压缩包内为1个doc格式文档大小6.07MB。文档详细介绍了Zjhis用户、系统信息、公用医院标识、公用代码、病人类别/性质、科室/病区代码、系统参数、职工与用户信息等表的字段定义与用途还包含住院病人信息、婴儿登记等扩展内容可作为电子病历数据库设计、数据治理和日常维护的重要参考。已有349人下载学习是医疗信息化项目实施中实用的工具性资料。1. 联众电子病历表结构文档一份让 EMR 联表查询少走弯路的底图接了三个月联众 EMR 的接口开发最难受的不是写业务代码而是每次想查“某个患者最近一次病历记录”都得靠猜病历表的主键到底是谁模板表和记录表是直接关联还是中间还有一张映射表状态字段里的数字含义文档里有没有写这份联众电子病历表结构文档解决的正是这些问题。它把二十来张核心表的字段、主外键关系、索引和常用查询路径一次讲清适合两类人一是刚接手联众 EMR 做二次开发或数据对接的工程师二是要写报表、做质控统计的医院信息科人员。文档是 doc 格式结构完整能直接作为建表评审、接口设计的参照底图。2. 先读懂这份 docER 图、表清单与字段命名的门道拿到一份表结构文档大部分人习惯直接翻字段列表我建议反过来先花半小时把 ER 图和外键关系捋顺再回头查字段。联众电子病历这套表结构核心表之间不是简单的父子关系而是围绕“患者、就诊、病历、模板”四条线交织在一起的没看懂关系就写 SQL后面基本要返工。2.1 文档内通常包含哪些内容这份 doc 文档虽然只有十几页但信息密度不低一般会按目录分成几个固定板块实体关系图ER 图在前然后是表清单再往后是逐表字段明细最后是索引和常用查询示例。我在实际拆分时建议你按下面这张表去定位内容板块内容使用场景ER 图表与表之间的关联关系设计联表查询、确认外键方向表清单表名、中文注释、用途分类快速判断业务功能对应哪张表字段明细字段名、类型、长度、是否为空、默认值、说明写 SQL、核对接口返回数据索引说明主键索引、普通索引、联合索引优化查询性能、避免全表扫描查询示例典型业务场景的简易 SQL复用到自己的报表或接口里doc 文档和在线文档不同的地方在于它没有超链接跳转所以我的习惯是先给每个板块做一个书签或者把 ER 图单独截图放在手边查表的时候反复对照。特别提醒一下doc 里的字段明细部分我遇到过中文字段注释和实际数据库注释对不上的情况这种情况以数据库实库为准doc 只作参照。2.2 ER 图先看主表优先抓三张核心表联众 EMR 的 ER 图我拆过多次画得还算规范但表一多就乱。我的方法是先找三类表病人主索引表、病历记录表、病历模板表。病人主索引表通常是整个 ER 图的起点几乎所有业务表都直接或间接引用它的主键病历记录表是收治过程中的事实表记录一次入院或一次门诊产生的病历内容病历模板表提供结构化定义记录表引用模板表的模板 ID 来约束内容格式。抓这三张表就够了其他辅助表比如科室表、医生表、字典表本质上是维度表只在需要展示中文名称时才关联。多数联众表结构文档里这三张表会出现在 ER 图的中心位置外键线也最密。如果你发现文档里的 ER 图没有直接画出外键线只是用字段名暗示关联那就要特别小心这种文档通常需要你自己拿 SQL 去验一遍实际外键关系。2.3 字段命名规则前缀表名、后缀表类型联众的表字段命名有一个容易上手的规律字段前缀通常与所属表名相关比如病历记录表里的字段多以“BR_”或“BL_”开头别名字段常以“BH”结尾编号。字段类型方面主键几乎都是整型自增业务编号用 VARCHAR时间字段用 DATETIME内容大文本用 TEXT 或 LONGTEXT。由于具体命名在各版本里可能有差异我一般会先看 ER 图旁边是否有“字段命名规范”说明有就以说明为准。这套命名规律的价值在于拿到新字段不用每次都翻表基本能猜个八九不离十。比如某表有字段 EMP_ID 和 DEPT_ID那大概率是关联员工表和部门表的维度字段。猜归猜最后建查询前仍然建议用DESC语句确认实际字段避免文档和实库不一致带来的“玄学报错”。3. 核心表拆解病人主索引、模板库与病历记录怎么连表结构文档最有价值的章节永远是核心表的字段明细。拆联众这套表结构时我的建议是先建一张核心表速查表把自己关心的表名、主键、关键业务字段集中列出来后面写 SQL 时不需要反复翻文档。下面把最常用的四张表过一遍字段模型按通用口径描述实际以文档和实库为准。3.1 病人主索引表整个 EMR 的入口病人主索引表承担的是患者身份唯一性职责联众 EMR 里通常叫 PATIENT_INDEX 或类似命名主键是 PID业务上可能还有门诊号、住院号、身份证号等统筹字段。这个表的字段设计有几个值得注意的地方CREATE TABLE patient_index ( pid BIGINT PRIMARY KEY COMMENT 病人主索引ID全局唯一, patient_no VARCHAR(32) NOT NULL COMMENT 院内患者编号跨就诊唯一, id_card VARCHAR(18) COMMENT 身份证号脱敏后存储, name VARCHAR(64) NOT NULL COMMENT 患者姓名, birth_date DATE COMMENT 出生日期, gender CHAR(1) COMMENT 性别1男 2女 9未知, create_time DATETIME COMMENT 建档时间, update_time DATETIME COMMENT 最后更新时间 );逻辑说明这张表最重要的约束是patient_no唯一它是业务层面识别患者的依据。id_card在很多医院场景里出于隐私保护会做加密或脱敏所以文档里如果写了明文存储你在做数据迁移时就要额外加一道脱敏处理。pid是全局主键所有子表的外键都用它。参数说明gender用单个字符存枚举值比用 VARCHAR 存“男/女”更省空间但可读性差查询时要做字典翻译。birth_date用 DATE 类型而不是 DATETIME是为了避免时间部分对统计造成干扰。3.2 病历模板表结构化的元数据中枢病历模板表保存的是病历的“格式定义”比如入院记录模板、出院小结模板、手术记录模板。联众 EMR 里模板表通常包含模板编码、模板名称、模板内容往往是 XML 或 JSON 结构、版本号以及生效状态。模板是病历记录表引用的核心维度版本管理做得不好会导致历史病历渲染错乱。CREATE TABLE emr_template ( template_id BIGINT PRIMARY KEY COMMENT 模板ID, template_code VARCHAR(32) NOT NULL COMMENT 模板编码业务唯一, template_name VARCHAR(128) NOT NULL COMMENT 模板名称, template_type CHAR(2) COMMENT 模板分类01入院 02出院 03手术 04病程, content_format VARCHAR(16) COMMENT 内容格式XML/JSON/TEXT, content_structure LONGTEXT COMMENT 模板结构定义XML或JSON, version_no INT DEFAULT 1 COMMENT 版本号每次修改自增, status TINYINT DEFAULT 1 COMMENT 状态0停用 1启用 );逻辑说明version_no字段是这套设计的灵魂。模板发生调整时不是直接更新记录行而是插入一条新模板并把版本号递增这样历史病历通过快照引用固定版本不会被后续模板调整影响。content_structure存的是结构定义不是实例内容所以用 LONGTEXT 完全够用。参数说明template_type用两位字符编码扩展性比 TINYINT 数字更直观。status是软删除思路停用的模板在业务查询里默认要加status 1过滤条件否则历史数据会被重复带出。3.3 病历记录表事实数据的主战场病历记录表是联众 EMR 里数据量增长最快的表每一次保存操作都可能落一条记录。它除了记录模板 ID、患者 ID、医生 ID还会保存病历内容快照以及状态字段。状态字段是最容易踩坑的因为不同版本的取值含义不一致有的用 0/1 表示草稿和完成有的用 1/2/3 表示暂存、提交、归档。CREATE TABLE emr_record ( record_id BIGINT PRIMARY KEY COMMENT 病历记录ID, pid BIGINT NOT NULL COMMENT 病人主索引ID关联patient_index, encounter_id BIGINT NOT NULL COMMENT 就诊ID一次住院或门诊对应一个, template_id BIGINT NOT NULL COMMENT 模板ID关联emr_template, record_content LONGTEXT COMMENT 病历内容快照XML或JSON, record_status TINYINT COMMENT 状态1暂存 2提交 3归档, doctor_id BIGINT COMMENT 经治医生ID, create_time DATETIME COMMENT 创建时间, submit_time DATETIME COMMENT 提交时间 );逻辑说明record_content是这份文档里最值钱的字段它存的是整个病历的结构化内容通常包含患者主诉、现病史、诊断等子项用 JSON 存储就是一套完整的病历数据。encounter_id把一次就诊下的多份病历串起来是统计“某次住院写了多少份病历”的关键关联键。参数说明查询时优先通过encounter_id过滤而不是pid因为一次住院会有多次病程记录按就诊维度查才能拿到完整链路。record_status的取值一定要在写 SQL 前用文档核对常见版本是 1 暂存、2 提交、3 归档但有些版本会用 A/B/C 存字符。3.4 三张表的串联路径现在把三张表放在一起看完整的关联关系从 patient_index 拿到患者通过 encounter 表就诊表找到一次具体的住院或门诊再通过 encounter_id 查到该次就诊下所有病历记录最后用 template_id 关联模板表获得病历的格式定义。这个链路的顺序是固定的中间不能跳步。我在拆文档时习惯把这条链路写成固定模板SELECT pi.name, r.record_id, t.template_name, r.record_status, r.create_time FROM emr_record r JOIN patient_index pi ON r.pid pi.pid JOIN emr_template t ON r.template_id t.template_id WHERE r.encounter_id 12345 ORDER BY r.create_time;逻辑说明这条 SQL 展示了三表关联的基本姿势。JOIN的方向是从事实表 emr_record 出发左连维度表这样查询基数最小。如果反过来从 patient_index 出发结果集会因为一对多关系被放大统计数字就会翻车。参数说明WHERE r.encounter_id 12345是核心过滤实际业务里 encounter_id 来自就诊表查询结果。ORDER BY r.create_time按时间排序保证病历按书写先后顺序展示。这里没有关联 encounter 表因为本次查询只需要一次就诊的数据直接指定 encounter_id 即可如果需要展示就诊类型和入出院时间再补 JOIN encounter。4. 从表结构到业务查询把文档翻译成可跑的 SQL表结构文档的价值不在“看”在“用”。下面拿三个真实场景来演示按就诊查全量病历、按模板统计科室完成率、跨表筛选患者。这三个查询覆盖了一大半日常开发需求你可以直接把 SQL 改改表名拿去用。4.1 场景一查某次就诊下所有病历及内容摘要临床科室最常问的问题是“这个患者这次住院都写了哪些病历”。按第 3 章的串联路径查询核心是 emr_record 表再把模板名称带出来SELECT r.record_id, t.template_name, r.record_status, CASE r.record_status WHEN 1 THEN 暂存 WHEN 2 THEN 提交 WHEN 3 THEN 归档 END AS status_text, LEFT(r.record_content, 80) AS content_preview, r.create_time FROM emr_record r LEFT JOIN emr_template t ON r.template_id t.template_id WHERE r.encounter_id 20240315001 ORDER BY r.create_time DESC;逻辑说明LEFT(r.record_content, 80)是对大字段做截断预览避免把整篇病历内容一次性拉出来导致网络传输过大。CASE WHEN把状态码翻译成人话这是接联众 EMR 报表时最常用的技巧。参数说明20240315001是就诊号实际场景中来自就诊查询。ORDER BY ... DESC把最新写的病历排最前。如果病历数量非常大建议在 emr_record 表上建(encounter_id, create_time)联合索引这条 SQL 的响应时间能从秒级降到毫秒级。4.2 场景二按模板维度统计各科室病历完成情况这个场景多用于质控统计核心是“提交并归档的病历算完成暂存的不算”。需要联合科室表和医生表SELECT d.dept_name, t.template_name, COUNT(r.record_id) AS total_cnt, SUM(CASE WHEN r.record_status 3 THEN 1 ELSE 0 END) AS finished_cnt, ROUND(SUM(CASE WHEN r.record_status 3 THEN 1 ELSE 0 END) / COUNT(r.record_id) * 100, 2) AS finish_rate FROM emr_record r JOIN emr_doctor doc ON r.doctor_id doc.doctor_id JOIN emr_dept d ON doc.dept_id d.dept_id JOIN emr_template t ON r.template_id t.template_id WHERE r.create_time 2025-01-01 00:00:00 AND r.create_time 2025-02-01 00:00:00 GROUP BY d.dept_name, t.template_name ORDER BY finish_rate ASC;逻辑说明这里把状态过滤放在SUM(CASE WHEN ...)内部而不是WHERE目的是保留总记录数再做条件统计。如果直接在 WHERE 里过滤状态就看不到分母了完成率会失真。参数说明时间范围用左闭右开写法 and 可以规避月末最后几秒的数据漏查。ROUND(x, 2)控制百分比小数位。ORDER BY finish_rate ASC升序是把完成率低的科室排前面方便质控重点关注。4.3 场景三跨表筛选含主诉关键词的患者病历内容存在 LONGTEXT 里联众常用 XML 或 JSON 格式保存。想查“主诉包含胸痛的患者”如果直接LIKE %胸痛%会误匹配到现病史、诊断等其他子项常见做法是先把 JSON 里的主诉字段提取出来SELECT pi.patient_no, pi.name, r.encounter_id, JSON_EXTRACT(r.record_content, $.chief_complaint) AS chief_complaint FROM emr_record r JOIN patient_index pi ON r.pid pi.pid WHERE r.template_id 1001 AND JSON_EXTRACT(r.record_content, $.chief_complaint) LIKE %胸痛% AND r.record_status 3;逻辑说明JSON_EXTRACT只取出 JSON 内容里的主诉字段避免 LIKE 误伤其他字段。template_id 1001限定在入院记录模板内因为只有入院记录才规范填写主诉病程记录里即使有主诉数据也不该参与检索。参数说明这段 SQL 依赖库版本对 MySQL 5.7 JSON 函数的支持。如果现场是 MySQL 5.5JSON_EXTRACT 用不了退路是取出 content 后在应用层解析或者查文档确认是否用 XML 格式存储XML 场景需要改用EXTRACTVALUE。条件里强制record_status 3避免把草稿里的模拟数据统计进临床数据。4.4 查询前必须确认的四个参数查联众 EMR 数据前我一般会先确认清楚状态字段的取值含义是否和文档一致、时间字段是DATETIME还是VARCHAR、内容字段是TEXT还是LONGTEXT、主键是BIGINT还是字符串这四点不确认就开写 SQL大概率要返工。实际经验是时间字段最容易出问题有些历史版本会用 VARCHAR 存2025-01-01 10:30:00导致范围查询走不上索引。确认手段就是DESC emr_record;看字段类型再SELECT COUNT(*) FROM emr_record WHERE create_time IS NULL;看脏数据量。5. 避坑字段文档与实库不一致的五个典型雷区联众电子病历的表结构文档我用下来最深的感受是文档能做向导不能做圣旨。下面这五个坑是在多个现场反复踩出来的每个都是“现象 → 原因 → 解决”的完整路径你对照着排雷即可。5.1 doc 里的字段名在实库里根本不存在现象按文档写SELECT emr_id FROM emr_record数据库直接报“Unknown column”。原因文档版本和现场部署版本不一致现场做过二次开发或补丁升级字段被改名或删除。解决先用DESC emr_record;拉出实库字段清单和 doc 做字段级比对。我一般会把实库字段导成 Excel和 doc 手工比对两遍重点看业务编号和时间字段。比对时注意大小写敏感性MySQL 在 Linux 下字段名区分大小写Windows 下不区分同一个库迁移到 Linux 上也可能踩这个坑。5.2 ER 图里画了外键实际 SQL 却跑不通关联现象ER 图上 emr_record 和 emr_doctor 明显连线JOIN 时却查不到数据。原因表间关联字段虽然存在但值域不匹配。常见情况是 emr_record.doctor_id 存的是工号字符串emr_doctor.doctor_id 是自增整型两边编码体系不同。解决不要轻信关联字段名相同就一定能 JOIN。先用两秒钟做值域验证SELECT doctor_id FROM emr_record LIMIT 5;和SELECT doctor_id FROM emr_doctor LIMIT 5;对比数据类型和样例值。如果一边是工号一边是自增 ID需要通过员工对照表转换或者直接用工号字段关联。这种问题在文档上看不出来只有跑数据才能发现。5.3 时间字段用 VARCHAR 存范围查询慢到怀疑人生现象WHERE create_time 2025-01-01执行了三十秒就差没超时。原因老版本表结构把时间存成 VARCHAR索引失效或者字段名为create_time实际是字符类型。解决如果字段确实是 VARCHAR 类型优先做数据治理把存量数据迁成 DATETIME 或 TIMESTAMP新写入由程序统一用参数化时间格式。迁移前先确认应用代码的读写逻辑是否依赖字符串格式比如DATE_FORMAT函数嵌套迁移后要同步改掉。日常查询可以临时用STR_TO_DATE(create_time, %Y-%m-%d %H:%i:%s)转换再比较但这种方式不会走索引大数据量下依然慢只能作为过渡方案。我的习惯是一旦确认时间字段是 VARCHAR就推动下个版本直接改表结构不要给历史包袱留情面。5.4 病历内容取出来是乱码或者是一大段转义字符现象record_content查出来是{\chief_complaint\:\...\}前端直接展示会出现斜杠和引号。原因内容字段可能存储了双重转义的数据或者程序写入时做了addslashes处理取出时没做反转义。解决先用SELECT LEFT(record_content, 200) FROM emr_record LIMIT 1;观察原始存储格式。如果是 JSON 带反斜杠用REPLACE(record_content, \\, )逐层清理如果是 PHP 老系统写入的可能还需要去掉\\r\\n。更稳妥的方案是写一个数据清洗脚本把历史数据统一转成标准 JSON 再覆盖回去但覆盖前必须备份表谨防误操作导致临床数据丢失。这类“内容扭曲”不是表结构问题是历史代码问题doc 文档里不会有任何说明。5.5 状态字段的魔法值文档里没写的取值范围现象按文档写的状态 3 过滤归档病历统计结果比科室手工数少了 20%单独查状态字段发现还有 4、5、9 等文档没提到的值。原因现场二次开发扩展了状态枚举比如新增 4 表示“已打印”5 表示“作废”或者 9 表示“异常关闭”字段注释没同步更新。解决凡是碰到状态字段必须先跑一遍全量枚举SELECT record_status, COUNT(*) FROM emr_record GROUP BY record_status;逻辑说明这条 SQL 能把状态分布一次性摊开哪些值有数据、哪些值文档没说明一目了然。看到文档没写的枚举值不要慌找到使用该状态的业务代码或问现场运维确认语义确认后把新枚举值补充到自己的状态字典表里。参数说明如果结果是 0 也算一种状态说明库里存在状态初始化的脏数据需要排除。统计时建议把枚举映射放在 SQL 外层或者视图层不要散落在各个报表 SQL 里以方便后续维护状态语义。6. 进阶用 information_schema 反向核验文档与实库的一致性表结构文档最怕的不是字段多而是文档和实库悄悄分家。我见过某医院联众 EMR 做完一次版本升级文档还是旧的结果报表系统跑错了一周才发现。从那以后我每次接手联众 EMR 相关项目都强制走一遍“文档 ⇄ 实库”字段比对再累也值。这一步可以直接用 SQL 把实库中的表结构拉出来再和 doc 字段明细逐条核对SELECT TABLE_NAME, COLUMN_NAME, COLUMN_TYPE, IS_NULLABLE, COLUMN_COMMENT FROM information_schema.COLUMNS WHERE TABLE_SCHEMA emr_db AND TABLE_NAME IN (emr_record, emr_template, patient_index) ORDER BY TABLE_NAME, ORDINAL_POSITION;逻辑说明information_schema.COLUMNS是 MySQL 自带的元数据表查出来的结果就是各表的真实字段定义。把这份结果导出成 Excel和 doc 字段明细逐列比对差异部分就是文档失真点。核对时重点看四列字段名、字段类型、是否可空、字段注释。参数说明TABLE_SCHEMA emr_db换成实际库名。比对时留意COLUMN_TYPE里的长度信息比如varchar(50)和varchar(255)在文档里可能都写作“字符型”但实际容量天差地别字符串长度不够会导致后续写入截断。如果现场环境是 Oracle 或 SQL Server对应查询换成ALL_TAB_COLUMNS或INFORMATION_SCHEMA.COLUMNS思路完全一致。手动比对费时但准确性最高如果你有 Python 环境也可以写个十行脚本自动做差集比对把差值输出成报告。我一般会把比对报告存档当作交接文档的一部分后续任何人接手都不用再从头猜。这套核验方法配合前面四章的拆解路径基本能把联众电子病历表结构文档的价值完整榨出来。核心就一句话先看 ER 图定链路再查字段理枚举最后用实库校验兜底。希望帮你少走两天弯路。本文还有配套的精品资源点击获取