2. 健康数据模型设计

健康评估与干预计划平台的数据来源天然是异构的:可穿戴设备持续上报心率、步数、睡眠分期;院内设备产生血压、血糖、体重等体征记录;用户填写PHQ-9、GAD-7等量表问卷;App埋点记录饮食、运动、用药等行为事件;环境传感器或第三方接口提供温湿度、空气质量、地理位置。若把这些数据直接堆进一张宽表,很快会遇到三个问题:同一指标单位与采集口径不一致、同一患者多次测量无法表达时间语义、问卷总分与条目得分无法同时保留。因此数据模型设计要同时解决实体关系、时序表达和标准映射三件事。

一、实体关系建模:以“人—事件—观测”为主干

推荐的主干结构是:Patient(受试者/患者)通过 Encounter(接触/评估场次)关联到 Observation(观测),观测再通过 ObservationComponent 表达多分量结果(如血压的收缩压/舒张压),通过 QuestionnaireResponse 表达问卷作答,通过 Device 表达采集来源。核心原则有三条:

  • 观测与定义分离:Observation 只存“谁、何时、什么值、什么单位、来自哪个设备/表单”,指标的定义(编码、正常范围、数据类型)放在 ObservationDefinition 或术语表中,避免每个观测重复携带元数据。
  • 问卷与量表分层:Questionnaire 定义题目与选项,QuestionnaireResponse 存单次作答,条目得分与总分分别落库,便于后续重算与审计。
  • 行为与环境作为事件流:行为(步数、饮食、用药)和环境(温湿度、PM2.5)本质是带时间戳的事件,用 Event 或 Observation 统一承载,避免为每类数据单独建表。

下面这张图给出多源数据到统一模型的映射关系,以及时序存储与标准映射的分层结构。

多源健康数据 体征数据 心率/血压/血糖/体重 问卷与量表 PHQ-9/GAD-7/自评 行为数据 步数/饮食/用药/睡眠 环境数据 温湿度/PM2.5/位置 统一实体模型(FHIR 风格) Patient 受试者/患者主索引 Encounter 评估场次/接触 Observation 观测值+单位+时间 QuestionnaireResponse 条目得分+总分 Device / Event 采集来源/行为事件 时序存储与标准映射 时序事实表 patient_id + code + effective_time + value 维度表 指标定义/单位/正常范围 设备/表单/来源系统 FHIR 资源映射 Observation / Questionnaire / Patient / Encounter OMOP CDM 映射 PERSON / VISIT_OCCURRENCE / MEASUREMENT / OBSERVATION 关键设计约束 1. 所有观测必须携带 effective_time(UTC 存储,展示时区转换),同一指标多次测量按时间序列存储,不覆盖。 2. 单位统一到 UCUM 编码,如血压 mmHg、血糖 mmol/L、步数 count,避免同指标多单位混存。 3. 问卷条目得分与总分分别落库,总分可由条目重算,保证可审计;量表版本号必须随作答记录保存。

二、时序数据建模:把“时间”作为一等公民

体征、行为、环境数据都是高频或中频时序数据。建模时不要只存“最新值”,而要保留完整时间序列。推荐采用“窄表 + 维度表”的方式:事实表只存 patient_id、code、effective_time、value_numeric、value_text、unit、source_id,维度表存指标定义、单位、正常范围、采集频率。这样新增指标不需要改表结构。

时序建模要明确三个约定:

  • 时间语义:区分 effective_time(生理事件发生时间)与 recorded_time(系统写入时间)。设备离线补传时两者可能相差数小时,评估时必须用 effective_time 做窗口聚合。
  • 采样与聚合:原始数据按设备采样频率存储,分析层再按小时/天聚合。例如心率原始值每5秒一条,日粒度分析时聚合为日均值、最大值、静息心率。
  • 缺失与异常:缺失值不补零,用 NULL 表示;异常值保留原始值并打质量标记(如 quality_flag = 'outlier'),避免清洗后无法追溯。

以血压为例,一次测量产生两条分量记录:收缩压 code=8480-6、舒张压 code=8462-4,共享同一 effective_time 和 encounter_id。查询“最近7天晨间血压均值”时,按 effective_time 过滤 06:00–10:00,按 patient_id + code 分组求均值,即可得到可用于干预计划判断的指标。

三、FHIR 与 OMOP 参考:映射而非照搬

FHIR 适合作为交换与接口层的参考模型。平台对外提供数据时,把内部 Observation 映射为 FHIR Observation 资源,字段对应关系为:内部 code → Observation.code(LOINC 编码),value_numeric + unit → valueQuantity,effective_time → effectiveDateTime,patient_id → subject.reference。问卷则映射为 QuestionnaireResponse,条目得分放在 item.answer.valueInteger,总分放在 item.linkId='total' 的扩展中。

OMOP CDM 适合作为分析层的参考模型。体征和实验室指标映射到 MEASUREMENT 表,问卷总分映射到 OBSERVATION 表,行为事件映射到 OBSERVATION 或 PROCEDURE_OCCURRENCE,环境暴露可映射到 OBSERVATION。关键字段是 person_id、measurement_concept_id、measurement_date、value_as_number、unit_concept_id。映射时要用标准术语集(LOINC、SNOMED、UCUM)做 concept_id 对齐,而不是自造编码。

实践建议:内部模型不必完全等于 FHIR 或 OMOP。推荐“内部统一模型 + 双向映射层”的结构:内部模型负责写入与查询性能,FHIR 映射层负责对外交换,OMOP 映射层负责科研分析。三层之间通过术语编码和映射表解耦,新增数据源时只扩展映射,不改动核心表。

四、一个可落地的建模检查清单

检查项 判断标准 常见问题
实体主键 Patient、Encounter、Observation 均有稳定业务主键 用自增ID做跨系统主键,导致合并困难
时间字段 effective_time 与 recorded_time 分离且均带时区 只存字符串时间,无法做窗口聚合
单位与编码 指标编码用 LOINC,单位用 UCUM 同一指标多套编码,无法跨源合并
问卷分层 条目得分与总分分别存储,保留量表版本 只存总分,无法重算或审计
映射可追溯 每条记录可回溯到原始来源与映射规则版本 映射后丢失来源,异常无法定位

完成上述设计后,平台在接入新数据源时只需回答三个问题:它属于哪类实体(观测、问卷、事件)?时间语义是什么?映射到哪个标准编码?把这三个问题固化到接入规范中,多源健康数据就能在统一模型下持续积累,为后续的健康评估与干预计划生成提供稳定、可追溯的数据底座。