4. 医疗数据模型与Schema演进

医疗湖仓的数据模型不是把医院数据库的表原样搬进数据湖,而是围绕临床与运营主题域重新组织。患者、就诊、医嘱、检验、影像、费用这六个主题域,构成了绝大多数医疗分析场景的骨架。理解它们之间的主键关系、时间语义和编码依赖,是批流一体处理能否落地的前提。

主题域建模:以业务事件为中心

主题域建模的核心是识别“业务事件”和“业务实体”。患者是实体,就诊是事件,医嘱是就诊内的事件,检验和影像是医嘱的执行结果,费用则是就诊与医嘱共同派生的财务事件。在湖仓中,通常采用“事实表+维度表”的宽表化思路,但医疗场景下更强调事件的时间戳和状态流转。

以就诊域为例,一次就诊至少包含:就诊标识(visit_id)、患者标识(patient_id)、就诊类型(门诊/急诊/住院)、入院时间、出院时间、科室、主治医生。医嘱域则包含:医嘱标识(order_id)、就诊标识、医嘱类型(药品/检验/检查/手术)、开立时间、执行时间、状态(已开立/已执行/已停止/已取消)。检验域通过申请单号与医嘱关联,包含标本类型、检验项目代码、结果值、单位、参考区间、危急值标志。影像域包含检查号、检查部位、影像号、报告状态。费用域包含收费项目、金额、医保支付类型、结算时间。

这些域之间不是简单的星型关系,而是存在多对多和时序依赖。例如,一条医嘱可能对应多条检验结果,一次就诊可能对应多条费用明细。在湖仓中,通常用“事件表+实体快照”的方式存储:事件表记录每次状态变化,实体快照记录当前最新状态。这样既能支持流式增量更新,也能支持历史回溯。

Schema-on-Read与Schema演进

医疗数据的Schema变化非常频繁。医院信息系统升级、检验项目新增、医保政策调整,都会导致字段增减或语义变化。如果采用Schema-on-Write,每次变更都要停机改表,这在批流一体场景下不可接受。因此,湖仓通常采用Schema-on-Read:写入时保留原始结构(如Parquet/ORC的列式存储),读取时由查询引擎根据元数据解析。

Schema演进需要解决三个问题:新增字段、删除字段、字段类型变更。在Delta Lake或Iceberg中,新增字段可以通过ALTER TABLE ADD COLUMN完成,历史数据该字段为NULL;删除字段通常用DROP COLUMN,但底层文件仍保留,读取时忽略;类型变更(如int转string)需要重写数据或使用兼容类型。对于流式写入,Schema Registry可以集中管理Avro/Protobuf的版本,消费者根据版本号解析。

实际工程中,建议为每个主题域维护一个“Schema版本表”,记录字段名、类型、生效时间、变更原因。当流式作业读取到新版本数据时,先检查Schema兼容性,再决定是否继续处理。不兼容的变更(如字段重命名)应通过新增字段+数据回填的方式过渡,而不是直接修改原字段。

医疗编码标准集成

ICD、SNOMED、LOINC是医疗数据互操作的三根支柱。ICD用于疾病诊断和手术操作分类,SNOMED用于临床术语的细粒度表达,LOINC用于检验项目和观察指标的标准化编码。在湖仓中,这些编码不是简单存成字符串,而是需要建立映射表和层级关系。

以检验域为例,医院内部检验项目代码(如“血常规-白细胞”)需要映射到LOINC代码(如“6690-2”)。映射表通常包含:本地代码、本地名称、标准代码、标准名称、映射置信度、生效时间。对于ICD,诊断编码需要支持多版本(ICD-9、ICD-10、ICD-11),并且要处理“主诊断/次诊断”的优先级。SNOMED则常用于症状、部位、严重程度等结构化字段。

集成时,建议在湖仓中建立“编码维表”,用标准代码作为主键,本地代码作为属性。查询时通过JOIN获取标准语义。对于流式数据,编码映射可以做成广播状态,避免每条记录都查外部数据库。同时,要保留原始编码和映射后的标准编码,便于追溯和审计。

医疗湖仓主题域关系与编码集成 患者域 patient_id · 姓名 · 性别 出生日期 · 医保类型 就诊域 visit_id · patient_id 类型 · 入院/出院时间 医嘱域 order_id · visit_id 类型 · 状态 · 开立时间 检验域 申请单号 · order_id LOINC · 结果值 · 单位 影像域 检查号 · order_id 部位 · 报告状态 费用域 收费项目 · 金额 医保支付 · 结算时间 1:N 1:N 检验类医嘱 检查类医嘱 费用明细 医疗编码标准集成层 ICD 疾病/手术分类 诊断编码 · 多版本 · 主/次诊断 SNOMED 临床术语 症状 · 部位 · 严重程度 LOINC 检验观察 项目代码 · 结果单位 · 参考区间 Schema-on-Read 演进策略:新增字段 → 历史NULL;删除字段 → 读取忽略;类型变更 → 兼容类型或重写 Schema版本表记录:字段名 · 类型 · 生效时间 · 变更原因 · 兼容性标记

批流一体的建模实践

在批流一体架构中,主题域建模需要同时考虑批处理的全量快照和流处理的增量变更。一个常见的做法是:用CDC(变更数据捕获)将医院数据库的binlog实时写入湖仓的“原始层”,然后在“明细层”按主题域做清洗和标准化,最后在“汇总层”构建宽表和指标。

以医嘱状态流转为例,原始层记录每次状态变更(开立→执行→停止),明细层按order_id聚合出当前状态和状态时间线,汇总层则计算“医嘱执行率”“平均执行时长”等指标。流处理负责实时更新状态,批处理负责历史回填和一致性校验。两者通过同一份Schema元数据协调,避免口径不一致。

编码集成应尽量前置到明细层。也就是说,在数据进入汇总层之前,就已经完成了ICD、SNOMED、LOINC的映射。这样上层分析可以直接使用标准编码,而不需要每次查询都做映射。映射表本身也可以作为维度表,通过流式广播或定期刷新保持最新。

实践建议:为每个主题域定义“最小可用字段集”,优先保证患者标识、就诊标识、时间戳、编码字段的完整性和一致性。Schema演进时,先在小范围主题域验证兼容性,再推广到全湖仓。编码映射的置信度低于阈值时,保留原始编码并标记待人工审核。

最后需要强调的是,医疗数据模型不是一次性设计出来的,而是随着业务和标准演进的。湖仓的价值在于它允许你“先存后治”——先把原始数据以低成本保留下来,再根据分析需求逐步完善Schema和编码映射。这种灵活性,正是批流一体处理在医疗场景中不可替代的原因。